当前位置: 首页 > news >正文

建设执业资格注册管理中心网站品牌网站建设熊掌号

建设执业资格注册管理中心网站,品牌网站建设熊掌号,wordpress更改icon,想在网站卖房怎么做14.6 Spring MVC 测试框架#xff08;每天翻译一点点#xff09; Spring MVC测试框架对 Spring MVC 代码提供一流的测试支持 #xff0c;它拥有一个 fluent API ,可以和JUnit, TestNG 或其它任何测试框架协同使用。 此测试框架基于 spring-test 模块的Servlet API mock obje… 14.6 Spring MVC 测试框架每天翻译一点点 Spring MVC测试框架对 Spring MVC 代码提供一流的测试支持 它拥有一个 fluent API ,可以和JUnit, TestNG 或其它任何测试框架协同使用。 此测试框架基于 spring-test 模块的Servlet API mock objects 的因此不需要依赖运行Servlet 容器来完成测试。它使用DispatcherServlet  来提供完整的Spring MVC运行时行为full Spring MVC runtime behavior并且支持加载项目实际使用的spring 配置通过TestContext框架实现。除此之外使用单例模式standalone mode创建的 controller 可以手动实例化一次只测试一个实例。  Spring MVC测试框架还提供了客户端Client-side测试支持使用RestTemplate。客户端测试可以模拟服务器的响应并且通常不需要启动服务器。 Spring Boot 框架可以编写完善的、端到端的集成测试包括对运行服务器的测试。假如你需要测试时同时测试服务器等运行环境  可以浏览一下 Spring Boot reference page. 更多关于容器外测试和端到端集成测试的区别请看 the section called “Differences between Out-of-Container and End-to-End Integration Tests”. 14.6.1 服务器端测试 很容易写一个普通的测试来测试Spring MVC 的controller使用JUnit或TestNG简单的实例化一个 controller并注入模拟依赖然后直接通过 MockHttpServletRequest, MockHttpServletResponse等调用 controller 的方法就可以了。然而这样一个测试方法还有很多东西没有测到比如请求映射数据绑定类型转换验证等等。 更有甚者, controller 的其它方法例如 InitBinder, ModelAttribute,和ExceptionHandler 也可能作为请求进程生命周期的一部分被调用。 Spring MVC 测试的目标是通过实际的 DispatcherServlet发送请求和生成响应为测试 controller 提供一个高效的途径。 Spring MVC Test builds on the familiar mock implementations of the Servlet API available in the spring-test module. 如此便可以在不用运行服务器的情况下发送请求和生成响应。绝大多数情况这都和运行在 runtime 毫无区别少数明显的区别参考 “基于容器测试和端到端集成测试的区别”. 下面是一个基于JUnit 的 Spring MVC 测试的例子。 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;RunWith(SpringJUnit4ClassRunner.class) WebAppConfiguration ContextConfiguration(test-servlet-context.xml) public class ExampleTests {Autowiredprivate WebApplicationContext wac;private MockMvc mockMvc;Beforepublic void setup() {this.mockMvc MockMvcBuilders.webAppContextSetup(this.wac).build();}Testpublic void getAccount() throws Exception {this.mockMvc.perform(get(/accounts/1).accept(MediaType.parseMediaType(application/json;charsetUTF-8))).andExpect(status().isOk()).andExpect(content().contentType(application/json)).andExpect(jsonPath($.name).value(Lee));}} 上面的测试依赖于TestContext框架的  WebApplicationContext 类的支持这个类用于从与被测代码同一个包下的 XML 配置文件中加载 Spring 的配置另外也支持 Java-based 或 Groovy-based 的配置方式参考 sample tests. 上面代码中的 MockMvc 类的实例是用来发送一个  GET 请求给 /accounts/1 然后验证响应的状态码是否 200响应类型是否是application/json, 以及响应主体是否有一个 name 属性它的值是 Lee. 代码中 jsonPath 的语法参考 Jayway 提供的 JsonPath project. 除上文提及的之外还许多请求响应结果的验证方式将会在下文讨论。 静态导入 上述例子中的 fluent API 需要导入一些静态依赖例如MockMvcRequestBuilders.*, MockMvcResultMatchers.*, 和MockMvcBuilders.*. 一个简单的找到这些类的方法是搜索匹配表达式MockMvc* 的类型。假如使用的是Eclipse一定要将这些项添加到“favorite static members”里在Eclipse的preference下的Java → Editor → Content Assist → Favorites里.这将使得你只需要输入这些静态方法的第一个字母content assist 就会帮你弹出方法全名。其它IDE (如 IntelliJ) 或许不需要任何额外配置只需要检查是否开启了对静态成员的自动代码完成功能 . 可选创建MockMvc实例的方式 现在主要有两种创建MockMvc类实例的方式。第一种是通过TestContext 框架加载Spring-MVC的配置文件然后将 WebApplicationContext 的对象注入到测试代码并用它作为初始化参数来构建一个  MockMvc 实例 RunWith(SpringJUnit4ClassRunner.class) WebAppConfiguration ContextConfiguration(my-servlet-context.xml) public class MyWebTests {Autowiredprivate WebApplicationContext wac;private MockMvc mockMvc;Beforepublic void setup() {this.mockMvc MockMvcBuilders.webAppContextSetup(this.wac).build();}// ...} 第二种只是手动创建测试控制器如new AccountController()而不用加载Spring配置文件。与默认配置方式不一样这种方式与Spring MVC基于Java类或MVC命名空间的配置方式很类似都是能自动创建上下文并且能通过配置进行定制。 public class MyWebTests {private MockMvc mockMvc;Beforepublic void setup() {this.mockMvc MockMvcBuilders.standaloneSetup(new AccountController()).build();}// ...} 你更青睐于使用哪种初始化方式呢? webAppContextSetup 方式加载的是你真正的Spring MVC配置文件因此是更纯粹更彻底的集成测试方法。而因为 TestContext  框架会缓存了已加载的Spring配置所以这种方式能让测试跑得更快即便它的测试套件test suite中包含了更多测试案例test case。更有用的是你可以通过Spring配置文件将mock出的service注入到Controller 中以此集中精力进行 web 层面的测试而不用分散精力来对付Service层。如下使用 Mockito 框架声明一个 mock 服务类 bean idaccountService classorg.mockito.Mockito factory-methodmockconstructor-arg valueorg.example.AccountService/ /bean 然后你可以将mock 服务类的对象注入到测试中用来构建Controller 以及验证异常 RunWith(SpringJUnit4ClassRunner.class) WebAppConfiguration ContextConfiguration(test-servlet-context.xml) public class AccountTests {Autowiredprivate WebApplicationContext wac;private MockMvc mockMvc;Autowiredprivate AccountService accountService;// ...} standaloneSetup 方式有点像单元测试它在某一个时间点只能测试一个Controller这个 Controller 也可以手动注入mock依赖并且它不会加载Spring配置文件。这种方式更关注于测试风格使得我们更容易辨别要测试的是哪个类而不用去管使用的是哪个配置文件。standaloneSetup 方式可以非常方便的写出 ad-hoc 测试用来验证代码特定特点或者调试某个问题。 和任何“集成测试和单元测试”之争一样没有谁对谁错。然而使用standaloneSetup 方式没有实现测试webAppContextSetup的需求也就没有办法验证你的Spring MVC配置是否正确。要想避免你就得所有测试都使用 webAppContextSetup  方式创建以便能测试到你真正的Spring MVC配置。 发送请求 使用perform()来发送任何HTTP请求都很简单 mockMvc.perform(post(/hotels/{id}, 42).accept(MediaType.APPLICATION_JSON)); 你也可以在perform()内部使用  MockMultipartHttpServletRequest   对象发送文件上传请求这样虽没有真正解析一个的 multipart 请求但看起来和真正解析了请求并没什么区别 mockMvc.perform(fileUpload(/doc).file(a1, ABC.getBytes(UTF-8))); 你也可以使用URI模板URI template的方式设置琴酒参数 mockMvc.perform(get(/hotels?foo{foo}, bar)); 或者以表单参数query of form parameters方式增加一个请求参数 mockMvc.perform(get(/hotels).param(foo, bar)); 如果程序依赖于请求的参数但没严格的explicitly 检查查询字符串这样的事经常发生那么不管你使用上面哪种方式来携带参数都不会出现问题。但需要注意的是如果使用URI模板方式这些参数将会被解码decoded而使用 param(…​) 方式的话则这些参数会被看做早已解码了也就不在解码了。  多数情况下我们都不需要考虑 context path 和 Servlet path。但是如果你确实需要测试完整的URI请确保设置好了相应的 contextPath和 servletPath  这样请求映射request mapping 才能起作用。 mockMvc.perform(get(/app/main/hotels/{id}).contextPath(/app).servletPath(/main)) 从上面的例子可见如果每发送一个模拟请求都设置一下 contextPath 和 servletPath那是相当的麻烦。所以你可以通过设置“请求的默认属性”来代替 public class MyWebTests {private MockMvc mockMvc;Beforepublic void setup() {mockMvc standaloneSetup(new AccountController()).defaultRequest(get(/).contextPath(/app).servletPath(/main).accept(MediaType.APPLICATION_JSON).build();} 上面例子中通过 mockMvc 设置的的属性对每个请求都起作用。假如一个属性设置了默认值但在某个请求里又被赋了新值那么新值就会覆盖掉默认值。这也是为什么在默认请求里设置的HTTP方法和URI没什么用因为每个请求都会重新指定它们。  定义运行结果期望Expectations 运行结果期望可以在perform()方法后附加一个或多个 .andExpect(..) 来定义 mockMvc.perform(get(/accounts/1)).andExpect(status().isOk()); MockMvcResultMatchers.*  类下面提供了一系列期望一些还嵌套了更具体的期望。 这些期望可以分为两大类。第一类断言验证响应的属性例如响应的状态响应头响应内容。对于断言来说这些响应结果是最重要的验证对象。  第二类断言放在响应的后面。这种类型的断言可以对Spring MVC的某些特殊对象进行检查。例如检查控制器的哪个方法处理了请求是否抛出了某个异常以及该异常是否已被处理检查模型对象的内容检查控制器返回了哪个视图或者是否添加了某个flash属性等等。除此之外这些断言也可以用来检查原生的Servlet的属性例如request和session的属性。 下面的测试使用第二类断言假设数据绑定或校验失败 mockMvc.perform(post(/persons)).andExpect(status().isOk()).andExpect(model().attributeHasErrors(person)); 通常在写测试的时候转储请求执行的结果是非常有用的通常是转储到控制台输出。转储操作的例子如下其中 print() 方法是从MockMvcResultHandlers静态引入的 mockMvc.perform(post(/persons)).andDo(print()).andExpect(status().isOk()).andExpect(model().attributeHasErrors(person)); 只要请求执行过程中没有抛出未处理的异常 print() 方法就会输出所有结果数据到System.out.打印出来。 SSpring 4.2包含一个 log() 方法和两个重载的 print() 方法,一个接受 OutputStream 作参数另外一个接受 Writer.作参数。例如使用 print(System.err) 会输出结果到 System.err;里打印使用 print(myWriter) 会输出结果到自定义的writer类里打印.如果你希望把请求执行的结果保存到日志里只需要调用 log() 方法就可以它会记录一条 DEBUG 级别的日志到org.springframework.test.web.servlet.result 目录下。 某些情况下我们的断言需要并且只能在请求执行结果中验证这可以通过在末尾添加一个 .andReturn()方法来实现 MvcResult mvcResult mockMvc.perform(post(/persons)).andExpect(status().isOk()).andReturn(); // ... 假如一个断言需要多次出现在不同的测试中则可以设置成公共断言绑定到 MockMvc 实例上: standaloneSetup(new SimpleController()).alwaysExpect(status().isOk()).alwaysExpect(content().contentType(application/json;charsetUTF-8)).build() 需要注意的是公共断言每次测试都会执行并且不会被在测试方法内部创建的独立的 MockMvc 对象覆盖掉。 如果控制器返回的是是JSON类型的响应响应体里面携带了由 Spring HATEOAS 创建的超链接。t则这个链接可以使用JsonPath表达式来验证 mockMvc.perform(get(/people).accept(MediaType.APPLICATION_JSON)).andExpect(jsonPath($.links[?(.rel self)].href).value(http://localhost:8080/people)); 如果控制器返回的是是XML类型的响应响应体里面携带了由 Spring HATEOAS创建的超链接则这个链接可以使用XPath表达式来验证 MapString, String ns Collections.singletonMap(ns, http://www.w3.org/2005/Atom); mockMvc.perform(get(/handle).accept(MediaType.APPLICATION_XML)).andExpect(xpath(/person/ns:link[relself]/href, ns).string(http://localhost:8080/people)); 注册过滤器 在配置好 MockMvc 实例后可以在其后注册若干个 Filter 实例: mockMvc standaloneSetup(new PersonController()).addFilters(new CharacterEncodingFilter()).build(); 已注册的过滤器可以通过 spring-test   包提供的 MockFilterChain 。来调用并且过滤器链的最后一个过滤器会被委托成 DispatcherServlet    基于容器测试和端到端集成测试的区别 前面曾提到过对Spring MVC的测试是基于 spring-test 模块提供的实现了Servlet API的mock对象的并且测试不需要启动Servlet容器。因此使用spring test来进行的基于容器测试与使用实际浏览器和服务器来进行的端到端测试是有很大差异的。 最简单的办法是从空白的 MockHttpServletRequest 入手。不去管你在请求里添加了什么也不用管这是一个什么样的请求。让人惊讶的是这里面没有默认的上下文路径没有包含jsessionid 的cookie,没有跳转、错误以及异步分派请求因此没有发生实际的jsp渲染。实际上“转发”和“重定向”的链接被保存到了MockHttpServletResponse 对象里面并且可以被断言。 这意味着如果使用jsp你可以验证jsp被哪个请求转发的但jsp里的html不会被渲染。换句话说JSP不会被调用。需要注意所有不依赖于转发的渲染技术例如 Thymeleaf, Freemarker, and Velocity 还是会渲染html作为响应体。对JSON、XML和其它格式的通过 ResponseBody ResponseBody注解标注的方法里返回的数据也一样。 另外你也可以考虑使用Spring Boot提供的 WebIntegrationTest.注解进行端到端的集成测试参看 Spring Boot reference. 两种测试方式各有利弊。Spring MVC Test测试方式不能简单认为是一种规模介于经典单元测试和集成测试之间的测试。确切的说Spring MVC Test测试方式不是一种经典的单元测试但和经典的单元测试有点像。例如你可以通过注入模拟的服务对象到控制器从而将web层独立出来这样你就可以通过 DispatcherServlet 使用项目真实的配置文件来对web层进行单独的测试。这在操作上已经和你从DAO的上一层来独立测试DAO层没区别了。你也可以使用单例模式启动来关注某个Controller在某个时间点的运行情况并且手动提供所需配置使其运行。 使用Spring MVC Test的另外一个重要区别是在概念上该测试是服务端测试因此你可以检查诸如哪个控制器被调用是否某个异常已经被HandlerExceptionResolver处理了模型的内容以及模型绑定的错误等。这使得编写一个断言变得很容易因为此时服务器端并不是像使用实际的HTTP客户端进行测试时那样是一个黑箱子是不可见的。 更多服务器端测试例子 框架本身的测试例子里包含许多例子演示如何使用Spring Test。你可以浏览这些示例获得更多灵感。同样在spring-mvc-showcase里面也有许多完全基于spring test的测试例子。 14.6.2 HtmlUnit 集成 Spring支持MockMvc和HtmlUnit.集成。它能简化使用html执行一个端到端测试的过程。集成之后开发者可以: 使用HtmlUnit, WebDriver和 Geb 等工具来测试html网页将变得很简单不需要再部署到服务器。可以测试页面内的javascript代码使用mock服务配置测试提高测试速度复用端到端测试和基于容器测试的逻辑。 MockMvc works with templating technologies that do not rely on a Servlet Container (e.g., Thymeleaf, Freemarker, Velocity, etc.), but it does not work with JSPs since they rely on the Servlet Container. 为什么使用 HtmlUnit 集成? 你可能会想为什么我需要用它答案是从一个最简单的程序里可以找到答案。假设你有一个Spring MVC网站程序这个程序有一个 Message 对象对象有CURD等操作。这个程序可以分页显示所有的消息你将如何测试它 使用Spring MVC Test假如我们是直接创建 Message对象的话这将是很简单的事。 MockHttpServletRequestBuilder createMessage post(/messages/).param(summary, Spring Rocks).param(text, In case you didnt know, Spring Rocks!);mockMvc.perform(createMessage).andExpect(status().is3xxRedirection()).andExpect(redirectedUrl(/messages/123)); 但是假如Message对象是从表单里创建的该如何测试举个例子假如我们的有表单如下 form idmessageForm action/messages/ methodpostdiv classpull-righta href/messages/Messages/a/divlabel forsummarySummary/labelinput typetext classrequired idsummary namesummary value /label fortextMessage/labeltextarea idtext nametext/textareadiv classform-actionsinput typesubmit valueCreate //div /form 如何保证表单会生成一个正确的请求来创建Message对象有人会天真的使用下面的方法 mockMvc.perform(get(/messages/form)).andExpect(xpath(//input[namesummary]).exists()).andExpect(xpath(//textarea[nametext]).exists()); 这个测试有明显的缺陷。假如我们更新了Controller将参数名 text修改成message 。我们的测试还会照常发送请求即使表单没有同步更新。对此我们可以使用拼接语句的方法如下 String summaryParamName summary; String textParamName text; mockMvc.perform(get(/messages/form)).andExpect(xpath(//input[name summaryParamName ]).exists()).andExpect(xpath(//textarea[name textParamName ]).exists());MockHttpServletRequestBuilder createMessage post(/messages/).param(summaryParamName, Spring Rocks).param(textParamName, In case you didnt know, Spring Rocks!);mockMvc.perform(createMessage).andExpect(status().is3xxRedirection()).andExpect(redirectedUrl(/messages/123)); 这会减少发送不正确的请求的风险但还是存在下列问题 假如页面包含多个表单该如何测试确实我们可以通过更新我们的xpath表达式来解决但这回会变得复杂得多了我们需要考虑更多因素例如表单域的类型是否正确表单域是否可用  另一个问题是我们要付出双倍的劳动。我们首先要验证视图然后提交视图附带刚刚验证过的参数。理论上这个可以一次完成。 最后这里还有一些我们没法考虑得事情。例如假如我们还想测试表单的Javascript数据校验代码怎么办 总的来说问题是测试网页不是针对单一的交互。相反的它掺杂了用户如何与网页交互以及网页如何和其它资源交互。举个例子表单视图的显示结果是用来给用户输入以创建Message对象。更有甚者我们的表单可能潜在使用了其它可能影响页面行为的资源例如javascript数据校验。 使用端到端集成测试来解救? 为解决上节所述问题我们可以使用端到端的集成测试但其同样有缺陷。考虑消息列表页其可以通过分页浏览多个消息。我们需要测试下列方面 当消息列表为空时页面是否会显示一个提示信息告诉用户当前没有结果可展示 页面是否正确显示一条消息 分页浏览是否正确 运行这些测试之前我们要确保我们的数据库已经包含正确的消息数据了。这有导致了一系列其它挑战。 确认数据库里的消息数据正确无误是一个乏味而繁琐的过程要考虑外键约束。 测试会变得很慢因为每次测试都要确认数据库无误。 因为数据库需要置于某个特定状态才能进行测试所以多个测试不能并行的进行。对自动生成的id、timesstamp很难进行断言 这些挑战并不意味着我们是要禁止所有的端到端测试而是通过重构复杂的测试使用执行更快更可靠并且没有边际效应的模拟服务我们能够减少端到端测试的次数。之后我们可以实施少数几次真正的端到端集成测试只用来验证简单的工作流以确保多个页面协同工作正常。 使用HtmlUnit集成测试 那么如何达到既能测试页面交互又能保持良好的测试性能呢答案是“把MockMvc和HtmlUnit集成起来”。 HtmlUnit集成选项 有多种方式可集成MockMvc 和HtmlUnit MockMvc加HtmlUnit:如果你想使用原生的HtmlUnit那就用这种方法。 MockMvc加WebDriver这种方式可以减少开发量重用集成和端到端测试的代码。MockMvc加 Geb假如你想在Groovy下测试可以使用这种方式同样减少开发量重用集成和端到端测试的代码。 MockMvc加 HtmlUnit 方式 本节介绍集成 MockMvc 与HtmlUnit。假如你想要使用原生的HtmlUnit库就可以使用这种方式。 集成MockMvc和HtmlUnit的启动 首先你要将依赖 net.sourceforge.htmlunit:htmlunit包含进来。为了在Apache HttpComponents4.5上使用你的HtmlUnit版本至少要2.18及以上。 使用 MockMvcWebClientBuilder 可以很容易的创建一个集成了 MockMvc  的HtmlUnit  WebClient 对象如下所示 Autowired WebApplicationContext context;WebClient webClient;Before public void setup() {webClient MockMvcWebClientBuilder.webAppContextSetup(context).build(); } This is a simple example of using MockMvcWebClientBuilder. For advanced usage see the section called “Advanced MockMvcWebClientBuilder” 上述代码可以确保任何指向 localhost 的URL会定向到 MockMvc 实例来处理而不会发生真实的HTTP连接。任何其它请求还是使用真实网络连接与普通浏览器一样。这样即使使用了CDN测试起来也很简单。 集成MockMvc和 HtmlUnit的用法 现在我们可以如使用普通浏览器一样使用HtmlUnit但不需要部署应用到服务器。例如我们可以用如下代码请求创建创新消息的视图 HtmlPage createMsgFormPage webClient.getPage(http://localhost/messages/form); The default context path is . Alternatively, we can specify the context path as illustrated in the section called “Advanced MockMvcWebClientBuilder”. 一旦获得 HtmlPage HtmlPage的引用我们就可以填充表单然后提交就可以创建一条消息。 HtmlForm form createMsgFormPage.getHtmlElementById(messageForm); HtmlTextInput summaryInput createMsgFormPage.getHtmlElementById(summary); summaryInput.setValueAttribute(Spring Rocks); HtmlTextArea textInput createMsgFormPage.getHtmlElementById(text); textInput.setText(In case you didnt know, Spring Rocks!); HtmlSubmitInput submit form.getOneHtmlElementByAttribute(input, type, submit); HtmlPage newMessagePage submit.click(); 最后我们可以检验新的消息是否创建成功。下面的断言使用了AssertJ 库。 assertThat(newMessagePage.getUrl().toString()).endsWith(/messages/123); String id newMessagePage.getHtmlElementById(id).getTextContent(); assertThat(id).isEqualTo(123); String summary newMessagePage.getHtmlElementById(summary).getTextContent(); assertThat(summary).isEqualTo(Spring Rocks); String text newMessagePage.getHtmlElementById(text).getTextContent(); assertThat(text).isEqualTo(In case you didnt know, Spring Rocks!); 这样做会在血多方面提升我们的MockMvc测试。首先我们不需要再先显式验证表单然后才能创建请求而且这个请求只是看起来和表单请求一样而已。现在我们只需要请求表单填充表单然后提交显著减少了额外工作。 另一个重要因素是HtmlUnit使用Mozilla Rhino engine来校验Javascript这意味着我们也可以测试页面包含的Javascript代码。. 更多HtmlUnit的使用方法请参看HtmlUnit参考文档。 MockMvcWebClientBuilder高级用法 在前面的例子中我们尽可能用最简单的方法使用 MockMvcWebClientBuilder 创建一个 WebClient 基于Spring TestContext框架加载的 WebApplicationContext。下面重复这个方法 Autowired WebApplicationContext context;WebClient webClient;Before public void setup() {webClient MockMvcWebClientBuilder.webAppContextSetup(context).build(); } 我们还可以指定更多配置选项。 WebClient webClient;Before public void setup() {webClient MockMvcWebClientBuilder// demonstrates applying a MockMvcConfigurer (Spring Security).webAppContextSetup(context, springSecurity())// for illustration only - defaults to .contextPath()// By default MockMvc is used for localhost only;// the following will use MockMvc for example.com and example.org as well.useMockMvcForHosts(example.com,example.org).build(); } 作为替代我们也可使用相同的启动过程通过配置一个独立的 MockMvc 实例并且应用到 MockMvcWebClientBuilder 中如下 MockMvc mockMvc MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();webClient MockMvcWebClientBuilder.mockMvcSetup(mockMvc)// for illustration only - defaults to .contextPath()// By default MockMvc is used for localhost only;// the following will use MockMvc for example.com and example.org as well.useMockMvcForHosts(example.com,example.org).build(); 配置起来是比较冗长的但是用 MockMvc 实例创建出 WebClient 之后我们就拥有了 MockMvc 的全部能力。 For additional information on creating a MockMvc instance refer to the section called “Setup Options”. MockMvc 加WebDriver方式 上一节我们知道了如何将MockMvc 和HtmlUint结合起来。这一节我们将利用另外一些 WebDriver定义的概念来使测试变得更容易。 为何要使用WebDriver 与MockMvc集成? 我们已经使用了HtmlUnit 和 MockMvc为什么还要去使用 WebDriver呢这是因为 WebDriver提供了一套非常优雅的API使我们能够很容易的组织代码。为了明白这点我们来看个例子。 Despite being a part of Selenium, WebDriver does not require a Selenium Server to run your tests. 假如我们现在需要确认消息已经被正确的创建测试的过程包括找到HTML表单的input元素填充然后编写多个断言。 这种方法会导致数不胜数的彼此独立的测试因为我们也要测试出错情况。例如要确认如果只是填充表单的一部分的话会抛出错误而填充整个表单则新创建的表单稍后会显示出来。 假如表单里有一个名为summary的域像那么下面的代码在我们测试中可能会经常出现 HtmlTextInput summaryInput currentPage.getHtmlElementById(summary); summaryInput.setValueAttribute(summary); 假如将 id 设为smmry会发生什么这会迫使我们修改所有测试方法来适应这次更改。这违反了DRY原则因此我们应该将这些相同的代码提取到一个方法中。 public HtmlPage createMessage(HtmlPage currentPage, String summary, String text) {setSummary(currentPage, summary);// ... }public void setSummary(HtmlPage currentPage, String summary) {HtmlTextInput summaryInput currentPage.getHtmlElementById(summary);summaryInput.setValueAttribute(summary); } 这就保证我们在改变UI的时候无需更新所有的测试方法。 还可再进一步的把这些代码逻辑放入当前网页的 HtmlPage 对象里去。 public class CreateMessagePage {final HtmlPage currentPage;final HtmlTextInput summaryInput;final HtmlSubmitInput submit;public CreateMessagePage(HtmlPage currentPage) {this.currentPage currentPage;this.summaryInput currentPage.getHtmlElementById(summary);this.submit currentPage.getHtmlElementById(submit);}public T T createMessage(String summary, String text) throws Exception {setSummary(summary);HtmlPage result submit.click();boolean error CreateMessagePage.at(result);return (T) (error ? new CreateMessagePage(result) : new ViewMessagePage(result));}public void setSummary(String summary) throws Exception {summaryInput.setValueAttribute(summary);}public static boolean at(HtmlPage page) {return Create Message.equals(page.getTitleText());} } 最初这个模式被熟知为“页面对象模式”。我们可以在使用HtmlUnit框架时使用该模式然而WebDriver框架提供了一些工具使得该模式更容易编写实现。下一节我们将探讨该模式。 集成MockMvc 和 WebDriver的启动 要在Spring MVC测试框架里使用WebDriver框架需先加入依赖org.seleniumhq.selenium:selenium-htmlunit-driver.到项目中。 我们可以很容易的使用 MockMvcHtmlUnitDriverBuilder 集成 WebDriver 和 MockMvc 代码如下 Autowired WebApplicationContext context;WebDriver driver;Before public void setup() {driver MockMvcHtmlUnitDriverBuilder.webAppContextSetup(context).build(); } This is a simple example of using MockMvcHtmlUnitDriverBuilder. For more advanced usage, refer to the section called “Advanced MockMvcHtmlUnitDriverBuilder” 上述代码可以确保任何指向 localhost 的URL会定向到 MockMvc 实例来处理而不会发生真实的HTTP连接。任何其它请求还是使用真实网络连接与普通浏览器一样。这样即使使用了CDN测试起来也很简单。 集成MockMvc 和 WebDriver的用法 现在我们可以如使用普通浏览器一样使用WebDriver但不需要部署应用到服务器。例如我们可以用如下代码请求创建创新消息的视图 CreateMessagePage page CreateMessagePage.to(driver); 我们可以填充表单然后提交以创建一条新的消息。 ViewMessagePage viewMessagePage page.createMessage(ViewMessagePage.class, expectedSummary, expectedText); 这个测试使用了页面对象模式改进了之前使用HtmlUnit的测试方法。正如在“为何要使用WebDriver 与MockMvc集成”章节里提到的我们可以在集成HtmlUnit框架后使用页面对象模式但在WebDriver里使用将会更容易。下面请看新的 CreateMessagePage 类的实现 public class CreateMessagePageextends AbstractPage { private WebElement summary;private WebElement text;FindBy(css input[typesubmit])private WebElement submit;public CreateMessagePage(WebDriver driver) {super(driver);}public T T createMessage(ClassT resultPage, String summary, String details) {this.summary.sendKeys(summary);this.text.sendKeys(details);this.submit.click();return PageFactory.initElements(driver, resultPage);}public static CreateMessagePage to(WebDriver driver) {driver.get(http://localhost:9990/mail/messages/form);return PageFactory.initElements(driver, CreateMessagePage.class);} } 你首先要注意的是CreateMessagePage 类继承自AbstractPage。我们不会详述AbstractPage类的细节但总的来说它包含了所有页面的公共方法。就好像假如应用程序里有一个导航条或者全局错误信息之类可以把这些逻辑可以放到一个共享的地方一样。 另外一个值得注意的地方是每个我们感兴趣的html部位都有一个成员变量与之对应。WebElement.WebDriver类型的页面工厂类通过自动解析每个网页元素允许我们移除大量HtmlUnit版本的CreateMessagePage 方法里的代码。PageFactory#initElements(WebDriver,ClassT)方法通过成员变量名称查找网页内所有元素的id和name自动解析每个网页元素. 我们可以使用 FindBy 注解覆盖默认的查找方式。我们的例子演示如何使用 FindBy 注解通过css选择器input[typesubmit]来查找提交按钮。 最后我们可以检验新消息已经创建成功。下面的断言使用了FEST assertion库。 assertThat(viewMessagePage.getMessage()).isEqualTo(expectedMessage); assertThat(viewMessagePage.getSuccess()).isEqualTo(Successfully created a new message); 可以看见 ViewMessagePage 实例允许我们和自定义域模型打交道。例如它公开了返回 Message 对象的方法。 public Message getMessage() throws ParseException {Message message new Message();message.setId(getId());message.setCreated(getCreated());message.setSummary(getSummary());message.setText(getText());return message; } 然后我们就可以在断言中使用该域对象。 最后测试完后别忘了关闭 WebDriver 的实例。 After public void destroy() {if (driver ! null) {driver.close();} } 更过使用WebDriver框架的信息请参看“WebDriver参考文档”。  MockMvcHtmlUnitDriverBuilder高级用法 在前面的例子中我们尽可能用最简单的方法使用 MockMvcHtmlUnitDriverBuilder 创建一个 WebDriver 对象基于Spring TestContext框架加载的 WebApplicationContext。下面重复这个方法 Autowired WebApplicationContext context;WebDriver driver;Before public void setup() {driver MockMvcHtmlUnitDriverBuilder.webAppContextSetup(context).build(); } 我们还可以指定更多配置选项。 WebDriver driver;Before public void setup() {driver MockMvcHtmlUnitDriverBuilder// demonstrates applying a MockMvcConfigurer (Spring Security).webAppContextSetup(context, springSecurity())// for illustration only - defaults to .contextPath()// By default MockMvc is used for localhost only;// the following will use MockMvc for example.com and example.org as well.useMockMvcForHosts(example.com,example.org).build(); } 作为替代我们也可使用相同的启动过程通过配置一个独立的 MockMvc 实例并且应用到 MockMvcHtmlUnitDriverBuilder 中如下 MockMvc mockMvc MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();driver MockMvcHtmlUnitDriverBuilder.mockMvcSetup(mockMvc)// for illustration only - defaults to .contextPath()// By default MockMvc is used for localhost only;// the following will use MockMvc for example.com and example.org as well.useMockMvcForHosts(example.com,example.org).build(); 配置起来是比较冗长的但是用 MockMvc 实例创建出 WebDriver 之后我们就拥有了 MockMvc 的全部能力。 For additional information on creating a MockMvc instance refer to the section called “Setup Options”. MockMvc 加 Geb方式 上一个章节我们已经知道如何使用 MockMvc 和 WebDriver本章节我们将使用Geb来使我们的测试更Groovy化。 为何要集成 Geb 与MockMvc? Geb框架是有WebDriver作为后盾的因此它的优点和WebDriver是一样的。Geb框架处理掉了样板代码使得测试更简洁更简单。 集成MockMvc和Geb方式启动 通过如下使用了 MockMvc 的 WebDriver 对象我们可以很容易的实例化一个Geb 框架的  Browser 对象。 def setup() {browser.driver MockMvcHtmlUnitDriverBuilder.webAppContextSetup(context).build() } This is a simple example of using MockMvcHtmlUnitDriverBuilder. For more advanced usage, refer to the section called “Advanced MockMvcHtmlUnitDriverBuilder” 上述代码可以确保任何指向 localhost 的URL会定向到 MockMvc 实例来处理而不会发生真实的HTTP连接。任何其它请求还是使用真实网络连接与普通浏览器一样。这样即使使用了CDN测试起来也很简单。 集成MockMvc和Geb的用法 现在我们可以如使用普通浏览器一样使用Geb但不需要部署应用到服务器。例如我们可以用如下代码请求创建创新消息的视图 to CreateMessagePage 我们可以填充表单然后提交以创建一条新的消息。 when: form.summary expectedSummary form.text expectedMessage submit.click(ViewMessagePage) 任何对不存在的方法的调用或对不存在的属性获取、引用都会转发给当前页面对象。这可以减少很多存在于直接使用WebDriver时的样板代码。 和直接使用WebDriver相比这个测试使用了页面对象模式改进了之前使用HtmlUnit的测试方法。正如之前提到的我们可以在集成HtmlUnit框架或WebDriver框架后使用页面对象模式但在Geb里使用将会更容易。下面请看基于Groovy 的新的 CreateMessagePage 类的实现 class CreateMessagePage extends Page {static url messages/formstatic at { assert title Messages : Create; true }static content {submit { $(input[typesubmit]) }form { $(form) }errors(required:false) { $(label.error, .alert-error)?.text() }} } 你首先要注意的是 CreateMessagePage 类继承自 Page。我们不会详述 Page类的细节但总的来说它包含了所有页面的公共方法。其次我们定义了一个访问该页面的URL,所以我们可以通过如下方式找到页面。 to CreateMessagePage 我们还定义了 at 代码块它可以判断我们当前页面是否是某个指定的页面。假如我们访问的是正确的页面它就会返回 true 。这也是为何我们可以在下面使用断言来断言我们是否访问了正确的页面。 then: at CreateMessagePage errors.contains(This field is required.) We use an assertion in the closure, so that we can determine where things went wrong if we were at the wrong page. 然后我们创建一个 content 代码块用来指定页面内所有感兴趣的区域。我们可以使用jQuery-ish Navigator API 来选择我们感兴趣的内容。 最后我们可以检验新消息是否创建成功。 then: at ViewMessagePage success Successfully created a new message id date summary expectedSummary message expectedMessage 更多关于如何最大化的使用Geb请参考Geb用户手册一书。 14.6.3 Client-Side REST Tests Client-side tests are for code using the RestTemplate. The goal is to define expected requests and provide stub responses: RestTemplate restTemplate new RestTemplate();MockRestServiceServer mockServer MockRestServiceServer.createServer(restTemplate); mockServer.expect(requestTo(/greeting)).andRespond(withSuccess(Hello world, MediaType.TEXT_PLAIN));// use RestTemplate ...mockServer.verify(); In the above example, MockRestServiceServer — the central class for client-side REST tests — configures the RestTemplate with a customClientHttpRequestFactory that asserts actual requests against expectations and returns stub responses. In this case we expect a single request to /greeting and want to return a 200 response with text/plain content. We could define as many additional requests and stub responses as necessary. Once expected requests and stub responses have been defined, the RestTemplate can be used in client-side code as usual. At the end of the testsmockServer.verify() can be used to verify that all expected requests were performed. Static Imports Just like with server-side tests, the fluent API for client-side tests requires a few static imports. Those are easy to find by searching MockRest*. Eclipse users should addMockRestRequestMatchers.* and MockRestResponseCreators.* as favorite static members in the Eclipse preferences under Java → Editor → Content Assist → Favorites. That allows using content assist after typing the first character of the static method name. Other IDEs (e.g. IntelliJ) may not require any additional configuration. Just check the support for code completion on static members. Further Examples of Client-side REST Tests Spring MVC Test’s own tests include example tests of client-side REST tests. 14.7 PetClinic Example The PetClinic application, available on GitHub, illustrates several features of the Spring TestContext Framework in a JUnit environment. Most test functionality is included in theAbstractClinicTests, for which a partial listing is shown below: import static org.junit.Assert.assertEquals; // import ...ContextConfiguration public abstract class AbstractClinicTests extends AbstractTransactionalJUnit4SpringContextTests {Autowiredprotected Clinic clinic;Testpublic void getVets() {CollectionVet vets this.clinic.getVets();assertEquals(JDBC query must show the same number of vets,super.countRowsInTable(VETS), vets.size());Vet v1 EntityUtils.getById(vets, Vet.class, 2);assertEquals(Leary, v1.getLastName());assertEquals(1, v1.getNrOfSpecialties());assertEquals(radiology, (v1.getSpecialties().get(0)).getName());// ...}// ... } Notes: This test case extends the AbstractTransactionalJUnit4SpringContextTests class, from which it inherits configuration for Dependency Injection (through theDependencyInjectionTestExecutionListener) and transactional behavior (through the TransactionalTestExecutionListener).The clinic instance variable — the application object being tested — is set by Dependency Injection through Autowired semantics.The getVets() method illustrates how you can use the inherited countRowsInTable() method to easily verify the number of rows in a given table, thus verifying correct behavior of the application code being tested. This allows for stronger tests and lessens dependency on the exact test data. For example, you can add additional rows in the database without breaking tests.Like many integration tests that use a database, most of the tests in AbstractClinicTests depend on a minimum amount of data already in the database before the test cases run. Alternatively, you might choose to populate the database within the test fixture set up of your test cases — again, within the same transaction as the tests. The PetClinic application supports three data access technologies: JDBC, Hibernate, and JPA. By declaring ContextConfiguration without any specific resource locations, the AbstractClinicTests class will have its application context loaded from the default location, AbstractClinicTests-context.xml, which declares a common DataSource. Subclasses specify additional context locations that must declare a PlatformTransactionManager and a concrete implementation of Clinic. For example, the Hibernate implementation of the PetClinic tests contains the following implementation. For this example, HibernateClinicTests does not contain a single line of code: we only need to declare ContextConfiguration, and the tests are inherited from AbstractClinicTests. Because ContextConfiguration is declared without any specific resource locations, the Spring TestContext Framework loads an application context from all the beans defined inAbstractClinicTests-context.xml (i.e., the inherited locations) and HibernateClinicTests-context.xml, with HibernateClinicTests-context.xmlpossibly overriding beans defined in AbstractClinicTests-context.xml. ContextConfiguration public class HibernateClinicTests extends AbstractClinicTests { } In a large-scale application, the Spring configuration is often split across multiple files. Consequently, configuration locations are typically specified in a common base class for all application-specific integration tests. Such a base class may also add useful instance variables — populated by Dependency Injection, naturally — such as aSessionFactory in the case of an application using Hibernate. As far as possible, you should have exactly the same Spring configuration files in your integration tests as in the deployed environment. One likely point of difference concerns database connection pooling and transaction infrastructure. If you are deploying to a full-blown application server, you will probably use its connection pool (available through JNDI) and JTA implementation. Thus in production you will use a JndiObjectFactoryBean or jee:jndi-lookup for the DataSource andJtaTransactionManager. JNDI and JTA will not be available in out-of-container integration tests, so you should use a combination like the Commons DBCPBasicDataSource and DataSourceTransactionManager or HibernateTransactionManager for them. You can factor out this variant behavior into a single XML file, having the choice between application server and a local configuration separated from all other configuration, which will not vary between the test and production environments. In addition, it is advisable to use properties files for connection settings. See the PetClinic application for an example.
http://www.pierceye.com/news/49153/

相关文章:

  • 共享网站的建设与规划wordpress主题模块添加
  • 做微商怎么通过网站和贴吧引流客源注册新公司流程和资料
  • 做性的网站汕头网络科技有限公司
  • 服装网站首页设计街道网站建设
  • 自己做的网站和模板青岛关键词优化报价
  • 界面官方网站.net商城网站开发
  • 网站建设有趣名称常平网站建设公司
  • 软件网站技术开发公司网站建设 管理规范
  • 青岛网站seo价格做网站运营需要具备哪些能力
  • 便宜的手机网站建设沈阳网站建设小志
  • asp 网站 购物车网站建设宣传册
  • 保定网站推广哪家好如何自己开发网站
  • 重庆网站优化排名推广cdr做图时怎么找到网站的
  • 网站模板源代码wordpress修页面链接
  • 教学直播平台网站建设费用域名最新通知
  • asp.net怎么做登录网站做婚介网站可行性报告模板
  • seo网站优化方法商务网站建设模板
  • 工信部网站备案怎么查资海网络一年做多少网站
  • 网站企业建设公司排名网站建设的实验的结论
  • 企业自建网站有哪些电子商务商城网站建设
  • 企业网站用什么系统好做电商一个月能挣多少钱
  • 建设一个微商的网站wordpress收不到邮件
  • 蛋糕方案网站建设做网站有名的公司
  • 网站运营托管咨询网站排名优化提升快速
  • 网站开发需要什么专业知识seo计费系统开发
  • 网站建设合同司法解释哪有做网站的 优帮云
  • 龙岗网站设计效果如何介绍设计的网站
  • 网站域名地址是什么广告设计公司朋友圈第一条怎么发
  • 程序员给女朋友做的网站三字型布局的网站
  • 江苏省义务教育标准化建设网站美橙域名查询网站