Spring MVC测试框架入门–第1部分

最新推出的主要Spring框架是Spring MVC测试框架,Spring Guys声称它是“一流的JUnit支持,可通过流畅的API测试客户端和服务器端Spring MVC代码” 1 。 在这个博客以及下一个博客中,我将看一看Spring的MVC测试框架,并将其应用于我现有的一些示例代码中,以弄清它是否能如其所愿。

已使用两种设置服务器端测试的方式来设计API。 首先,它们带有Spring上下文文件,其次,以编程方式没有上下文文件。 Spring的Guy将该程序化方法称为“独立”模式。

以编程方式设置测试似乎更类似于单元测试,并且最好用于对特定的控制器类进行独立于其协作者的单元测试。 另一方面,加载Spring上下文文件的操作实际上是集成测试,并且更适合端到端测试。

您可以在此处找到有关测试技术的博客的完整列表。

如果您像我一样,那么您已经在使用现有的框架(例如Mockito或Easymock)来测试您的控制器。 通常的Mockito / Easymock方法是实例化您的控制器,注入模拟或存根依赖性,然后调用被测方法,注意返回值或验证模拟方法调用。

Spring Mvc Test框架与其他模拟框架采用不同的方法,因为它加载Spring DispatcherServlet来模拟Web容器的操作。 然后,将被测试的控制器加载到Spring上下文中,并由DispatcherServlet对其进行访问,就像在“现实生活”中一样。

这种方法的好处是,它允许您将控制器作为控制器而不是POJO进行测试。 这意味着将处理并考虑控制器的注释,执行验证并以正确的顺序调用方法。

您是否同意这种方法,并取决于您对测试技术的看法。 如果您认为应该将测试的每个类/方法都隔离到第n个级别,并且每个测试都应完全原子化,那么这可能不适合您。 如果您比较务实,并且可以将测试控制器的好处看作是……一个控制器,那么可能会对这个框架感兴趣。

与Mockito和Easymock的方法有所不同,缺点是代码看起来与这些较旧的,已建立的技术不同。 它在很大程度上依赖于构建器模式来构造匹配器,请求构建器和处理程序,一旦掌握了一切,这一切都是有道理的。 我怀疑使用构建器模式的动机是为了简化模拟HttpServletRequest的设置以及对模拟HttpServletResponse对象的询问,这在定义上可能非常棘手。

在本博客中,我将看一下Spring API的编程/独立技术,并将其与类似的基于Mockito的单元测试进行比较。

为了加快处理速度,我使用了Blue Peter方法“这是我之前准备的方法”,并从我的Facebook博客中获取了FacebookPostsController ,我将为此编写两个单元测试类:第一个使用Mockito,另一个使用Spring Mvc测试API。

控制器代码如下所示:

@Controller 
public class FacebookPostsController { private static final Logger logger = LoggerFactory .getLogger(FacebookPostsController.class); @Autowired private SocialContext socialContext; @RequestMapping(value = "posts", method = RequestMethod.GET) public String showPostsForUser(HttpServletRequest request, HttpServletResponse response, Model model) throws Exception { String nextView; if (socialContext.isSignedIn(request, response)) { List<Post> posts = retrievePosts(); model.addAttribute("posts", posts); nextView = "show-posts"; } else { nextView = "signin"; } return nextView; } private List<Post> retrievePosts() { Facebook facebook = socialContext.getFacebook(); FeedOperations feedOps = facebook.feedOperations(); List<Post> posts = feedOps.getHomeFeed(); logger.info("Retrieved " + posts.size() + " posts from the Facebook authenticated user"); return posts; } 
}

我不打算讲这段代码的背景,因为它可以在Facebook博客中找到 。 但是,总而言之,Facebook示例应用程序访问用户的Facebook帐户并在示例应用程序中显示其新闻提要。 为此, FacebookPostsController检查SocialContext类,以确定用户是否已登录其Facebook帐户。 如果用户登录到其Facebook帐户,则控制器将检索用户的帖子并将其添加到模型中以进行显示。 另一方面,如果用户未登录,则将他们定向到登录页面。

两个单元测试类中的每一个都将包含三个公共方法:
我将依次检查setup()testShowPostsForUser_user_is_not_signed_intestShowPostsForUser_user_is_signed_in

如您所料,测试testShowPostsForUser_user_is_not_signed_intestShowPostsForUser_user_is_signed_in用于测试用户登录和未登录其Facebook帐户的情况。

“标准” Mockito测试

@Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); instance = new FacebookPostsController(); ReflectionTestUtils.setField(instance, "socialContext", socialContext); }

设置代码相当简单,包含三个简单步骤:

  1. 使用MockitoAnnotations.initMocks(this)初始化模拟对象。
  2. 创建一个新的FacebookPostsController实例,它是被测试的对象。
  3. 将模拟的SocialContext注入FacebookPostsController
@Test public void testShowPostsForUser_user_is_not_signed_in() throws Exception { when(socialContext.isSignedIn(request, response)).thenReturn(false); String result = instance.showPostsForUser(request, response, model); assertEquals("signin", result); }

testShowPostsForUser_user_is_not_signed_in方法将模拟的SocialContext配置为在调用其isSignedIn()方法时返回false 。 这意味着剩下要做的就是断言showPostsForUser(...)方法返回"signin"将用户定向到登录页面。

@Test public void testShowPostsForUser_user_is_signed_in() throws Exception { when(socialContext.isSignedIn(request, response)).thenReturn(true); when(socialContext.getFacebook()).thenReturn(facebook); when(facebook.feedOperations()).thenReturn(feedOps); List<Post> posts = Collections.emptyList(); when(feedOps.getHomeFeed()).thenReturn(posts); String result = instance.showPostsForUser(request, response, model); verify(model).addAttribute("posts", posts); assertEquals("show-posts", result); }

testShowPostsForUser_user_is_signed_in稍微复杂一些。 在将模拟的SocialContext配置为在调用其isSignedIn()方法时返回true ,还有另外四行代码可确保从模拟Facebook提要中返回posts列表并将其添加到模拟Model 。 调用showPostsForUser(...)之后,需要完成两个附加步骤:验证模拟Model包含posts列表,以及断言showPostsForUser(...)的返回值为"show-posts"

接下来,Spring MVC Test框架代码; 但是,在开始之前,您需要将以下依赖项添加到POM文件:

<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>${org.springframework-version}</version><scope>test</scope></dependency>

Spring MVC测试

Spring MVC测试框架版本运行相同的两个测试,但是方式不同……

@Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); FacebookPostsController instance = new FacebookPostsController(); ReflectionTestUtils.setField(instance, "socialContext", socialContext); mockMvc = MockMvcBuilders.standaloneSetup(instance).build(); }

如果看一下上面的代码,就可以看到,就setup(...)而言,它看起来与上面的直接Mockito代码非常相似。 像基于Mockito的测试一样,第一步是使用初始化模拟对象
MockitoAnnotations.initMocks(this) ,然后创建一个FacebookPostsController的新实例,将模拟的SocialContext注入到该实例中。 但是,这一次, FacebookPostsController状态已降级为局部变量,因为设置的重点是创建一个Spring的MockMvc实例,该实例用于执行测试。 该mockMvc是通过调用创建MockMvcBuilders.standaloneSetup(instance).build()其中instanceFacebookPostsController对象,我们正在测试。

@Test public void testShowPostsForUser_user_is_not_signed_in() throws Exception { HttpServletRequest request = anyObject(); HttpServletResponse response = anyObject(); when(socialContext.isSignedIn(request, response)).thenReturn(false); MockHttpServletRequestBuilder getRequest = get("/posts").accept(MediaType.ALL); ResultActions results = mockMvc.perform(getRequest); results.andExpect(status().isOk()); results.andExpect(view().name("signin")); }

与Mockito版本类似, testShowPostsForUser_user_is_not_signed_in方法将模拟的SocialContext配置为在调用其isSignedIn()方法时返回false 。 这次,下一步是使用静态方法创建一个称为MockHttpServletRequestBuilder东西。
MockMvcRequestBuilders.get(...)和构建器模式。 它被传递到mockMVC.perform(...)方法中,在此方法中,该方法用于创建MockHttpServletRequest对象,该对象用于定义测试的起点。 在此测试中,我要做的只是传递"/posts" URL并将输入设置为“ any”媒体类型。 您可以使用诸如contentType()contextPath()cookie()等方法配置许多其他请求对象属性。有关更多信息,请查看Spring Javadoc中的MockHttpServletRequest

mockMvc.perform()方法返回一个ResultActions对象。 这似乎是实际MvcResult的包装。 ResultsActions是一个便捷对象,用于以与JUnit的assertEquals(...)或Mockito的verify(..)方法相同的方式声明测试结果。 在这种情况下,我正在检查结果Http状态是否正常(即200),并且下一个视图将"signin"

此测试与仅Mockito版本之间的区别在于,您不是直接测试对测试实例的方法调用的结果; 您正在测试方法调用生成的HttpServletResponse对象。

@Test public void testShowPostsForUser_user_is_signed_in() throws Exception { HttpServletRequest request = anyObject(); HttpServletResponse response = anyObject(); when(socialContext.isSignedIn(request, response)).thenReturn(true); when(socialContext.getFacebook()).thenReturn(facebook); when(facebook.feedOperations()).thenReturn(feedOps); List<Post> posts = Collections.emptyList(); when(feedOps.getHomeFeed()).thenReturn(posts); mockMvc.perform(get("/posts").accept(MediaType.ALL)).andExpect(status().isOk()) .andExpect(model().attribute("posts", posts)) .andExpect(view().name("show-posts")); }

testShowPostsForUser_user_is_signed_in方法的Spring Test版本与Mockito版本非常相似,该测试是通过将SocialContext.isSignedIn()方法配置为返回true以及feedOps.getHomeFeed()配置为返回posts列表来准备测试的。 此方法的Spring Mvc Test部分与上述testShowPostsForUser_user_is_not_signed_in版本几乎相同,不同之处在于,这次它使用andExpect(view().name("show-posts")检查下一个视图名称"show-posts"而不是"sign-in" andExpect(view().name("show-posts")我在这里使用的代码样式与我在上面使用的样式有些不同,并且是Spring的Guy偏爱的样式。如果您能在Github上找到更多这种样式的示例,持有Spring MVC Showcase应用。

那么,您可以从此比较中得出什么结论? 公平地说,这不是真正的比较– Spring MVC Test API在建立标准Mockito技术的基础上,采用了不同的方法,创建了一个框架,旨在仅在其本机运行时环境的模型中对Spring MVC控制器进行测试。 。

这对您是否有用,我会让您决定。 它确实具有将控制器视为控制器而不是POJO的优点,这意味着它们已经过更彻底的测试。 从个人的角度来看,我喜欢加载Spring配置文件并将其用于集成测试的想法,这将在我的下一个博客中介绍。

  • 1请参阅: http : //static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/testing.html#spring-mvc-test-framework
  • 该博客的代码可在GitHub上找到: https : //github.com/roghughe/captaindebug/tree/master/facebook

参考: Spring的MVC测试框架入门–来自我们的JCG合作伙伴 Roger Hughes的第1部分 ,位于Captain Debug的Blog博客上。

翻译自: https://www.javacodegeeks.com/2013/07/getting-started-with-springs-mvc-test-framework-part-1.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/367644.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

metaclass

用metaclass来指定类C的元类是MyTypeclass MyType:def __init__(cls, *args, **kwargs):print(here!)#由于metaclassMyType&#xff0c;所以执行到这一步的时候&#xff0c;会调用MyType的构造函数 class C(metaclassMyType):def __init__(self):pass对象后面跟()&#xff0c;是…

Bootstrap中的条纹进度条使用案例

创建一个条纹的进度条的步骤如下&#xff1a;1.添加一个带有 class .progress 和 .progress-striped 的 <div>2.接着在上面的 <div> 内&#xff0c;添加一个带有 class .progress-bar 和 class progress-bar-* 的空的 <div>。其中&#xff0c;* 可以是succes…

LM拟合算法

一、 Levenberg-Marquardt算法 &#xff08;1&#xff09;ya*e.^(-b*x)形式拟合 clear all % 计算函数f的雅克比矩阵&#xff0c;是解析式 syms a b y x real; fa*exp(-b*x); Jsymjacobian(f,[a b]); % 拟合用数据。参见《数学试验》&#xff0c;p190&#xff0c;例2 % data_1…

java的前生今世_HBaseGC的前生今世-身世篇

网易视频云是网易倾力打造的一款基于云计算的分布式多媒体处理集群和专业音视频技术&#xff0c;提供稳定流畅、低时延、高并发的视频直播、录制、存储、转码及点播等音视频的PAAS服务&#xff0c;在线教育、远程医疗、娱乐秀网易视频云是网易倾力打造的一款基于云计算的分布式…

CapeDwarf – Java EE上的Google App Engine

我有很多爱好。 从早期的Java EE规范一路走来&#xff0c;并通过Java EE 7进行了“云”之旅&#xff0c;我很好奇看到新宣布的CapeDwarf项目有哪些库存&#xff0c;可以在内部引入Google的平台即服务&#xff0c;提供“ Google App Engine ” 。 到目前为止的故事 我确实使用了…

windows 服务中托管asp.net core

在windows 服务中托管asp.net core SDK 2.1.300 官方示例 1、添加运行标识符xml <PropertyGroup> <TargetFramework>netcoreapp2.1</TargetFramework> <RuntimeIdentifier>win7-x64</RuntimeIdentifier> </PropertyGroup> 2、添加包引用 d…

Bootstrap插件之-按钮插件

按钮 button.js 按钮的功能很丰富。通过控制按钮的状态或创建一组按钮并形成一些新的组件&#xff0c;例如工具条。 跨浏览器兼容性 在页面多次加载之间&#xff0c;Firefox 仍然保持表单控件的状态&#xff08;禁用状态和选择状态&#xff09;。一个解决办法是设置autocomplet…

第二周读书笔记——《构建之法》

【对一些实例的看法】 “我写了二叉树的遍历算法实现&#xff0c;在这里&#xff0c;二叉树是数据结构&#xff0c;遍历的实现细节是算法。C程序就是结果。但是这个程序有什么实际用处呢&#xff1f;在Java和其他一些语言中&#xff0c;似乎没有指针&#xff0c;那我可以不必了…

java springmvc 数据库事务_事务的简单回顾_JavaEE框架(Maven+SpringMvc+Spring+MyBatis)全程实战教程_Java视频-51CTO学院...

SpringMVCSpring MVC属于SpringFrameWork的后续产品&#xff0c;已经融合在Spring Web Flow里面。Spring MVC 分离了控制器、模型对象、分派器以及处理程序对象的角色&#xff0c;这种分离让它们更容易进行定制。SpringSpring是一个开源框架&#xff0c;Spring是于2003 年兴起的…

异步重试模式

当您有一段经常失败且必须重试的代码时&#xff0c;此Java 7/8库提供了丰富且不引人注目的API&#xff0c;并提供了针对此问题的快速且可扩展的解决方案&#xff1a; ScheduledExecutorService scheduler Executors.newSingleThreadScheduledExecutor(); RetryExecutor execu…

前端 ---jQuery的补充

15-jQuery补充 jquery内容补充 jquery除了咱们上面讲解的常用知识点之外&#xff0c;还有jquery 插件、jqueryUI知识点 jqueryUI 官网&#xff1a; https://jqueryui.com/ jqueryUI 中文网&#xff1a; http://www.jqueryui.org.cn/ jquery插件内容包含 官网demo&#xff1a; h…

[软件工程]自我介绍----一个小菜鸡的自我介绍(C++版 手动偷笑~)

#include “iostream” using namespace std; class Lee : protected MyMother,protected MyFather { private:string ChineseName("李怡龙");string EnglishName("Lee");int age 20;string university("青海大学计算机系");string level("…

用Java编写Hadoop MapReduce任务

尽管Hadoop框架本身是使用Java创建的&#xff0c;但MapReduce作业可以用许多不同的语言编写。 在本文中&#xff0c;我将展示如何像其他Java项目一样&#xff0c;基于Maven项目在Java中创建MapReduce作业。 准备示例输入 让我们从一个虚构的商业案例开始。 在这种情况下&#…

java 大二学期总结报告_大二学生自我总结「」

大二学生导师工作总结转眼间&#xff0c;我们的大二学年就这样结束了&#xff0c;在迎接新的一学期前我们来写一份自我总结吧。下面是小编搜集整理的大二学生自我总结&#xff0c;欢迎阅读。更多资讯尽在自我总结栏目!大二学生自我总结回顾大学二年,通过良师的教导和自身的刻苦…

windows下揪出java程序占用cpu很高的线程

背景 天天搞java&#xff0c;这些监控也都知道&#xff0c;用过&#xff0c;但也没往细里追究。因为也没碰见这种问题&#xff0c;这次还是静下来走一遍流程吧。与网上基本一致&#xff0c;不过我区分了下linux和windows的不一样。我感觉基本是程序写成死循环了或者大对象分配多…

jquery -input事件

input输入框的change事件&#xff0c;要在input失去焦点的时候才会触发 $(input[namemyInput]).change(function() { ... }); 在输入框内容变化的时候不会触发change&#xff0c;当鼠标在其他地方点一下才会触发用下面的方法会生效&#xff0c;input [html] view plain copy$(&…

Bootstrap中的下拉列表

下拉列表&#xff08;select&#xff09;注意&#xff0c;很多原生选择菜单单 - 即在 Safari 和 Chrome 中 - 的圆角是无法通过修改 border-radius 属性来改变的。复制<select class"form-control"><option>1</option><option>2</option&…

Spring集成:轻量级集成方法

当今的应用程序希望能够访问企业环境中的所有业务&#xff0c;而无需考虑与绝望的系统无缝集成的应用程序技术。 可以通过使用中间件技术对各种系统进行布线来实现这种集成。 集成平台使应用程序可以相互共享信息的环境&#xff0c;从而使体系结构具有高度的互操作性。 Spring…

动态REM

什么是rem&#xff1f; rem是相对于根元素html字体大小来计算的&#xff0c;即( 1rem html字体大小 ) rem和em区别&#xff1f; rem:&#xff08;root em&#xff0c;根em&#xff09;根元素的fort-size的大小计算em&#xff1a;相对长度单位&#xff0c;相对于当前对象内文本…

java教学楼的属性_java设计一个父类建筑物building,由它派生出教学楼类classroom,然后采用一些数据进行测试....

public class Building {public String bname;//建筑物名称public int floors;//代表总层数public double area;//代表总面积public Building(){}public Building(String bname, int floors, double area) {this.bname bname;this.floors floors;this.area area;}}public cl…