5.1测试Spring MVC的控制器
在前面的章节中,我们创建了我们项目,但是我们应该怎样知道它的执行是我们想要的结果。更重要的是,我们应用怎么知道过了6个月或1年之后,这些应用还可以执行良好。这个问题的解决就是通过一系列的测试代码。
在第四章节中,我们自定义了starter,现在我们需要将这些代码移除。现在我们需要创建基础测试去测试我们的应用,确保所有的控制器都会暴露出RESTful URLs的信息。这人测试就是我们所知道的Unit Testing,我们要确保所以测试相关的bean要初始化并关联在一起。这种类型的测试就是我们所涉及的Integration或Service Testing.
5.1.1代码实现
Spring Boot已经给了我们测试文件,BookPubApplicationTests.java,在src/test/java/org/owen/bookpub文件夹下。内容如下:
@SpringBootTest class BookPubApplicationTests {
@Test void contextLoads() { }
} |
- 在build.gradle文件中,我们添加spring-boot-starter-test的包依赖。
implementation 'org.springframework.boot:spring-boot-starter-test' |
- 接着,我们来扩展我们基础模板BookPubApplicatonTests的类。
/* * Spring Boot 1.0使用注释 * @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = BookPubApplication.class) @WebIntegrationTest("server.port:0") */ @RunWith(SpringRunner.class) @SpringBootTest(classes = BookPubApplication.class) //测试环境使用,用来表示测试环境使用的ApplicationContext将是WebApplicationContext类型的 @WebAppConfiguration public class BookPubApplicationTests { @Autowired private WebApplicationContext context; @Autowired private BookRepository repository; @Value("${local.server.port}") private int port; private MockMvc mockMvc; private TestRestTemplate restTemplate = new TestRestTemplate();
/** * 加载环境 */ @Before public void setupMockMvc() { mockMvc = MockMvcBuilders.webAppContextSetup(context).build(); }
/** * 添加数据到数据库 */ @Test public void contextLoads() { assertEquals(1, repository.count()); }
/** * 这个调用首先是要开户应用,然后通过getForObject的http地址访问应用 * 问题:由于使用h2数据库,所以开启应用后就不能再创建表了。所以测试不会通过。 * 解决:需要判断判断H2的如果有表了就不需要创建表了。 * * 如果把应用关闭了,然后会连接不到应用,也是测试不通过 */ @Test public void webappBookIsbnApi() { Book book = restTemplate.getForObject("http://localhost:" + port + "/books/978-1-78528-415-1", Book.class); assertNotNull(book); assertEquals("Packt", book.getPublisher().getName()); }
/** * 这个测试最后.andExpect(jsonPath("name").value("Packt"))这个测试不通过,因为是返回的数据类型是 * List的数组,不为Json。 * @throws Exception */ @Test public void webappPublisherApi() throws Exception { mockMvc.perform(get("/publishers")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.parseMediaType("application/json"))) .andExpect(content().string(containsString("Packt"))) //.andExpect(jsonPath("name").value("Packt")) ; } } |
- 为了在运行时使用使用jsonPath(…),我们需要添加如下的依赖在我们build.gradle
testRuntime("com.jayway.jsonpath:json-path") |
- 然后执行测试类,选择Gradle Test运行。在控制台,你将会看到如下的信息。
:compileJava :compileTestJava :testClasses :test 2020-04-13 21:40:44.694 INFO 25739 --- [ Thread-4] ationConfigEmbeddedWebApplicationContext : Closing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebAp plicationContext@206f4aa6: startup date [Mon Apr 13 21:40:36 CDT 2020]; root of context hierarchy 2020-04-13 21:40:44.704 INFO 25739 --- [ Thread-4] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default' 2020-04-13 21:40:44.705 INFO 25739 --- [ Thread-4] org.hibernate.tool.hbm2ddl.SchemaExport : HHH000227: Running hbm2ddl schema export 2020-04-13 21:40:44.780 INFO 25739 --- [ Thread-4]org.hibernate.tool.hbm2ddl.SchemaExport : HHH000230: Schema export complete BUILD SUCCESSFUL Total time: 24.635 secs |
- 通过build/reports/tests/index.html,你将会看到详细信息:
- 点击org.test.bookpb.BookPubApplicationTests会看到每个测试的情况和测试的时间长。
- 我们了可以点击Standard output的按钮去查看更多的运行日志。
5.1.2代码说明
注意:以下说明是对Spring Boot1.x版本的。
首先,我们看到如下的注释在BookPubApplication的类中:
- @RunWith(SpringJUnit4ClassRunner.class):这个是标准JUnit注释以至于我们可以使用SpringJUnit4ClassRunner提供的Spring Test Context框架的标准JUnit测试。
- @SringApplicationConfiguration(classes = BookPubApplication.class):这是Spring Boot注释,这个注释决定如何去加载和配置Spring Allicaton Context到的有的测试中。这个注释里包含ContextConfiguration注释,这个结构说明测试框架去使用Spring Boot的SpringApplicationContextLoader给应用。
- @WebIntegrationTest(“server.port:0”):这个注释是说明Spring Boot是当前完全测试和要求完全初始化和启动应用,毕竟是真实的处理。这个注释经常与@SpringApplicationConfiguration一起用,为了完全测试。server.port:0告诉Spring Boot去启动Tomcat时使用随机的http端口,并且随后会将值放入到我们声明的@Value(“${local.server.port}”) private int port。当你测试在Jenkins或CI服务中,这个随机的http端口是非常方便的,不然后,你有多个作业同步运行,你会的端口冲突。
接着,我们来看一下代码。因为是Spring Boot的测试,我们可以通过@Autowired自动装配任何对象,或者使用@Value放入特殊的环境值。在我们的测试中,我们自动加载了WebApplicatonContext和BookRepository的对象,这些对象将会用在执行标准的JUnit@Test注释的方法中。
在第一个测试方法contextLoads(),我们仅仅测试我们数据库后BookRepository的向数据库中插入信息。
第二个测试方法,我们测试RESTful URL。我们使用TestRestTemplate和调用RESTful,并且使用的 随机的端口。
我们用MockMvc去替换第二测试的方法。这是由Spring Test框架提供的,允许我们不需要客户端为基础的测试,即不需要需要我们启动项目然后去测试。但是测试的结果是基于服务端测试的,不需要启动项目,我们就可以与第二个方法相似的测试。
为了使用MockMvc,我伴着引入了@Autowired WebApplicationContext,并且使用MockMvcBuilders的工具去创建一个实例。我们将在启动时执行,这样就不需要每次测试都执行。
MockMvc提供给我们许多的能力去执行测试,这些都与web应用有关。这样的设计与方法链有关,允许我们使用连接多种的测试。我们使用如下的例子:
- perform(get(…))方法是开启web的应用。这我们的例子中,我们应用GET的请求,但是MockMvcRequestBuilders类提供的是表述的函数给每次的方法调用。
- andExpect(…)方法可以多次被使用,每次的请求都是一次评估。这样的调用是实现了接口MockMvcMatchers的静态灶。这些使用了大量不同的检查,例如:响应状态,内容类型,值储蓄在session中,明确重发等。我们将会使用第三方的包json-path,为了去测试JSON的返回值,确保它包含了正确的元素。
5.1.3问题
接着,我们来看一下代码。因为是Spring Boot的测试,我们可以通过@Autowired自动装配任何对象,或者使用@Value放入特殊的环境值。在我们的测试中,我们自动加载了WebApplicatonContext和BookRepository的对象,这些对象将会用在执行标准的JUnit@Test注释的方法中。
在第一个测试方法contextLoads(),我们仅仅测试我们数据库后BookRepository的向数据库中插入信息。
第二个测试方法,我们测试RESTful URL。我们使用TestRestTemplate和调用RESTful,并且使用的 随机的端口。
- 在测试时会遇到Json列循环的问题,即报错误信息为: Infinite recursion (StackOverflowError).这个是要在实例对象加上如下说明: 项目中ManyToOne 需要加上@JsonBackReference , ManyToMany或OneToMany 需要加上@JsonManagedReference
- 测试方法webappBookIsbnApi()会有问题,原因是:这个调用首先是要开户应用,然后通过getForObject的http地址访问应用问题:由于使用h2数据库,所以开启应用后就不能再创建表了。所以测试不会通过。
- 第三个测试中.andExpect(jsonPath("name").value("Packt")),是有问题,因为我们的操返回的不是JSON类型
5.1测试Spring MVC的控制器
在前面的章节中,我们创建了我们项目,但是我们应该怎样知道它的执行是我们想要的结果。更重要的是,我们应用怎么知道过了6个月或1年之后,这些应用还可以执行良好。这个问题的解决就是通过一系列的测试代码。
在第四章节中,我们自定义了starter,现在我们需要将这些代码移除。现在我们需要创建基础测试去测试我们的应用,确保所有的控制器都会暴露出RESTful URLs的信息。这人测试就是我们所知道的Unit Testing,我们要确保所以测试相关的bean要初始化并关联在一起。这种类型的测试就是我们所涉及的Integration或Service Testing.
5.1.1代码实现
Spring Boot已经给了我们测试文件,BookPubApplicationTests.java,在src/test/java/org/owen/bookpub文件夹下。内容如下:
@SpringBootTest
class BookPubApplicationTests {
@Test
void contextLoads() {
}
}
- 在build.gradle文件中,我们添加spring-boot-starter-test的包依赖。
-
implementation 'org.springframework.boot:spring-boot-starter-test'
- 接着,我们来扩展我们基础模板BookPubApplicatonTests的类。
-
/*
* Spring Boot 1.0使用注释
* @RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = BookPubApplication.class)
@WebIntegrationTest("server.port:0")
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = BookPubApplication.class)
//测试环境使用,用来表示测试环境使用的ApplicationContext将是WebApplicationContext类型的
@WebAppConfiguration
public class BookPubApplicationTests
{
@Autowired
private WebApplicationContext context;
@Autowired
private BookRepository repository;
@Value("${local.server.port}")
private int port;
private MockMvc mockMvc;
private TestRestTemplate restTemplate = new TestRestTemplate();
/**
* 加载环境
*/
@Before
public void setupMockMvc()
{
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
/**
* 添加数据到数据库
*/
@Test
public void contextLoads()
{
assertEquals(1, repository.count());
}
/**
* 这个调用首先是要开户应用,然后通过getForObject的http地址访问应用
* 问题:由于使用h2数据库,所以开启应用后就不能再创建表了。所以测试不会通过。
* 解决:需要判断判断H2的如果有表了就不需要创建表了。
*
* 如果把应用关闭了,然后会连接不到应用,也是测试不通过
*/
@Test
public void webappBookIsbnApi()
{
Book book = restTemplate.getForObject("http://localhost:" + port
+ "/books/978-1-78528-415-1", Book.class);
assertNotNull(book);
assertEquals("Packt", book.getPublisher().getName());
}
/**
* 这个测试最后.andExpect(jsonPath("name").value("Packt"))这个测试不通过,因为是返回的数据类型是
* List的数组,不为Json。
* @throws Exception
*/
@Test
public void webappPublisherApi() throws Exception
{
mockMvc.perform(get("/publishers"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.parseMediaType("application/json")))
.andExpect(content().string(containsString("Packt")))
//.andExpect(jsonPath("name").value("Packt"))
;
}
}
- 为了在运行时使用使用jsonPath(…),我们需要添加如下的依赖在我们build.gradle
-
testRuntime("com.jayway.jsonpath:json-path")
- 然后执行测试类,选择Gradle Test运行。在控制台,你将会看到如下的信息。
-
:compileJava
:compileTestJava
:testClasses
:test
2020-04-13 21:40:44.694 INFO 25739 --- [ Thread-4]
ationConfigEmbeddedWebApplicationContext : Closing
org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebAp
plicationContext@206f4aa6: startup date [Mon Apr 13 21:40:36 CDT 2020];
root of context hierarchy
2020-04-13 21:40:44.704 INFO 25739 --- [ Thread-4]
j.LocalContainerEntityManagerFactoryBean : Closing JPA
EntityManagerFactory for persistence unit 'default'
2020-04-13 21:40:44.705 INFO 25739 --- [ Thread-4]
org.hibernate.tool.hbm2ddl.SchemaExport : HHH000227: Running hbm2ddl
schema export
2020-04-13 21:40:44.780 INFO 25739 --- [ Thread-4]org.hibernate.tool.hbm2ddl.SchemaExport : HHH000230: Schema export
complete
BUILD SUCCESSFUL
Total time: 24.635 secs
- 通过build/reports/tests/index.html,你将会看到详细信息:
- 点击org.test.bookpb.BookPubApplicationTests会看到每个测试的情况和测试的时间长。
- 我们了可以点击Standard output的按钮去查看更多的运行日志。
-
5.1.2代码说明
注意:以下说明是对Spring Boot1.x版本的。
首先,我们看到如下的注释在BookPubApplication的类中:
- @RunWith(SpringJUnit4ClassRunner.class):这个是标准JUnit注释以至于我们可以使用SpringJUnit4ClassRunner提供的Spring Test Context框架的标准JUnit测试。
- @SringApplicationConfiguration(classes = BookPubApplication.class):这是Spring Boot注释,这个注释决定如何去加载和配置Spring Allicaton Context到的有的测试中。这个注释里包含ContextConfiguration注释,这个结构说明测试框架去使用Spring Boot的SpringApplicationContextLoader给应用。
- @WebIntegrationTest(“server.port:0”):这个注释是说明Spring Boot是当前完全测试和要求完全初始化和启动应用,毕竟是真实的处理。这个注释经常与@SpringApplicationConfiguration一起用,为了完全测试。server.port:0告诉Spring Boot去启动Tomcat时使用随机的http端口,并且随后会将值放入到我们声明的@Value(“${local.server.port}”) private int port。当你测试在Jenkins或CI服务中,这个随机的http端口是非常方便的,不然后,你有多个作业同步运行,你会的端口冲突。
5.1测试Spring MVC的控制器
在前面的章节中,我们创建了我们项目,但是我们应该怎样知道它的执行是我们想要的结果。更重要的是,我们应用怎么知道过了6个月或1年之后,这些应用还可以执行良好。这个问题的解决就是通过一系列的测试代码。
在第四章节中,我们自定义了starter,现在我们需要将这些代码移除。现在我们需要创建基础测试去测试我们的应用,确保所有的控制器都会暴露出RESTful URLs的信息。这人测试就是我们所知道的Unit Testing,我们要确保所以测试相关的bean要初始化并关联在一起。这种类型的测试就是我们所涉及的Integration或Service Testing.
5.1.1代码实现
Spring Boot已经给了我们测试文件,BookPubApplicationTests.java,在src/test/java/org/owen/bookpub文件夹下。内容如下:
@SpringBootTest class BookPubApplicationTests {
@Test void contextLoads() { }
} |
在build.gradle文件中,我们添加spring-boot-starter-test的包依赖。
implementation 'org.springframework.boot:spring-boot-starter-test' |
/* * Spring Boot 1.0使用注释 * @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = BookPubApplication.class) @WebIntegrationTest("server.port:0") */ @RunWith(SpringRunner.class) @SpringBootTest(classes = BookPubApplication.class) //测试环境使用,用来表示测试环境使用的ApplicationContext将是WebApplicationContext类型的 @WebAppConfiguration public class BookPubApplicationTests { @Autowired private WebApplicationContext context; @Autowired private BookRepository repository; @Value("${local.server.port}") private int port; private MockMvc mockMvc; private TestRestTemplate restTemplate = new TestRestTemplate();
/** * 加载环境 */ @Before public void setupMockMvc() { mockMvc = MockMvcBuilders.webAppContextSetup(context).build(); }
/** * 添加数据到数据库 */ @Test public void contextLoads() { assertEquals(1, repository.count()); }
/** * 这个调用首先是要开户应用,然后通过getForObject的http地址访问应用 * 问题:由于使用h2数据库,所以开启应用后就不能再创建表了。所以测试不会通过。 * 解决:需要判断判断H2的如果有表了就不需要创建表了。 * * 如果把应用关闭了,然后会连接不到应用,也是测试不通过 */ @Test public void webappBookIsbnApi() { Book book = restTemplate.getForObject("http://localhost:" + port + "/books/978-1-78528-415-1", Book.class); assertNotNull(book); assertEquals("Packt", book.getPublisher().getName()); }
/** * 这个测试最后.andExpect(jsonPath("name").value("Packt"))这个测试不通过,因为是返回的数据类型是 * List的数组,不为Json。 * @throws Exception */ @Test public void webappPublisherApi() throws Exception { mockMvc.perform(get("/publishers")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.parseMediaType("application/json"))) .andExpect(content().string(containsString("Packt"))) //.andExpect(jsonPath("name").value("Packt")) ; } } |
testRuntime("com.jayway.jsonpath:json-path") |
:compileJava :compileTestJava :testClasses :test 2020-04-13 21:40:44.694 INFO 25739 --- [ Thread-4] ationConfigEmbeddedWebApplicationContext : Closing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebAp plicationContext@206f4aa6: startup date [Mon Apr 13 21:40:36 CDT 2020]; root of context hierarchy 2020-04-13 21:40:44.704 INFO 25739 --- [ Thread-4] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default' 2020-04-13 21:40:44.705 INFO 25739 --- [ Thread-4] org.hibernate.tool.hbm2ddl.SchemaExport : HHH000227: Running hbm2ddl schema export 2020-04-13 21:40:44.780 INFO 25739 --- [ Thread-4]org.hibernate.tool.hbm2ddl.SchemaExport : HHH000230: Schema export complete BUILD SUCCESSFUL Total time: 24.635 secs |
5.1.2代码说明
注意:以下说明是对Spring Boot1.x版本的。
首先,我们看到如下的注释在BookPubApplication的类中:
接着,我们来看一下代码。因为是Spring Boot的测试,我们可以通过@Autowired自动装配任何对象,或者使用@Value放入特殊的环境值。在我们的测试中,我们自动加载了WebApplicatonContext和BookRepository的对象,这些对象将会用在执行标准的JUnit@Test注释的方法中。
在第一个测试方法contextLoads(),我们仅仅测试我们数据库后BookRepository的向数据库中插入信息。
第二个测试方法,我们测试RESTful URL。我们使用TestRestTemplate和调用RESTful,并且使用的 随机的端口。
我们用MockMvc去替换第二测试的方法。这是由Spring Test框架提供的,允许我们不需要客户端为基础的测试,即不需要需要我们启动项目然后去测试。但是测试的结果是基于服务端测试的,不需要启动项目,我们就可以与第二个方法相似的测试。
为了使用MockMvc,我伴着引入了@Autowired WebApplicationContext,并且使用MockMvcBuilders的工具去创建一个实例。我们将在启动时执行,这样就不需要每次测试都执行。
MockMvc提供给我们许多的能力去执行测试,这些都与web应用有关。这样的设计与方法链有关,允许我们使用连接多种的测试。我们使用如下的例子:
5.1.3问题
们用MockMvc去替换第二测试的方法。这是由Spring Test框架提供的,允许我们不需要客户端为基础的测试,即不需要需要我们启动项目然后去测试。但是测试的结果是基于服务端测试的,不需要启动项目,我们就可以与第二个方法相似的测试。
为了使用MockMvc,我伴着引入了@Autowired WebApplicationContext,并且使用MockMvcBuilders的工具去创建一个实例。我们将在启动时执行,这样就不需要每次测试都执行。
MockMvc提供给我们许多的能力去执行测试,这些都与web应用有关。这样的设计与方法链有关,允许我们使用连接多种的测试。我们使用如下的例子:
5.1.3问题
- 接着,我们来扩展我们基础模板BookPubApplicatonTests的类。
- 为了在运行时使用使用jsonPath(…),我们需要添加如下的依赖在我们build.gradle
- 然后执行测试类,选择Gradle Test运行。在控制台,你将会看到如下的信息。
- 通过build/reports/tests/index.html,你将会看到详细信息:
- 点击org.test.bookpb.BookPubApplicationTests会看到每个测试的情况和测试的时间长。
- 我们了可以点击Standard output的按钮去查看更多的运行日志。
- @RunWith(SpringJUnit4ClassRunner.class):这个是标准JUnit注释以至于我们可以使用SpringJUnit4ClassRunner提供的Spring Test Context框架的标准JUnit测试。
- @SringApplicationConfiguration(classes = BookPubApplication.class):这是Spring Boot注释,这个注释决定如何去加载和配置Spring Allicaton Context到的有的测试中。这个注释里包含ContextConfiguration注释,这个结构说明测试框架去使用Spring Boot的SpringApplicationContextLoader给应用。
- @WebIntegrationTest(“server.port:0”):这个注释是说明Spring Boot是当前完全测试和要求完全初始化和启动应用,毕竟是真实的处理。这个注释经常与@SpringApplicationConfiguration一起用,为了完全测试。server.port:0告诉Spring Boot去启动Tomcat时使用随机的http端口,并且随后会将值放入到我们声明的@Value(“${local.server.port}”) private int port。当你测试在Jenkins或CI服务中,这个随机的http端口是非常方便的,不然后,你有多个作业同步运行,你会的端口冲突。
- perform(get(…))方法是开启web的应用。这我们的例子中,我们应用GET的请求,但是MockMvcRequestBuilders类提供的是表述的函数给每次的方法调用。
- 在测试时会遇到Json列循环的问题,即报错误信息为: Infinite recursion (StackOverflowError).这个是要在实例对象加上如下说明: 项目中ManyToOne 需要加上@JsonBackReference , ManyToMany或OneToMany 需要加上@JsonManagedReference
- 测试方法webappBookIsbnApi()会有问题,原因是:这个调用首先是要开户应用,然后通过getForObject的http地址访问应用问题:由于使用h2数据库,所以开启应用后就不能再创建表了。所以测试不会通过。
- 第三个测试中.andExpect(jsonPath("name").value("Packt")),是有问题,因为我们的操返回的不是JSON类型
- andExpect(…)方法可以多次被使用,每次的请求都是一次评估。这样的调用是实现了接口MockMvcMatchers的静态灶。这些使用了大量不同的检查,例如:响应状态,内容类型,值储蓄在session中,明确重发等。我们将会使用第三方的包json-path,为了去测试JSON的返回值,确保它包含了正确的元素。
- perform(get(…))方法是开启web的应用。这我们的例子中,我们应用GET的请求,但是MockMvcRequestBuilders类提供的是表述的函数给每次的方法调用。
- andExpect(…)方法可以多次被使用,每次的请求都是一次评估。这样的调用是实现了接口MockMvcMatchers的静态灶。这些使用了大量不同的检查,例如:响应状态,内容类型,值储蓄在session中,明确重发等。我们将会使用第三方的包json-path,为了去测试JSON的返回值,确保它包含了正确的元素。
- 在测试时会遇到Json列循环的问题,即报错误信息为: Infinite recursion (StackOverflowError).这个是要在实例对象加上如下说明: 项目中ManyToOne 需要加上@JsonBackReference , ManyToMany或OneToMany 需要加上@JsonManagedReference
- 测试方法webappBookIsbnApi()会有问题,原因是:这个调用首先是要开户应用,然后通过getForObject的http地址访问应用问题:由于使用h2数据库,所以开启应用后就不能再创建表了。所以测试不会通过。
- 第三个测试中.andExpect(jsonPath("name").value("Packt")),是有问题,因为我们的操返回的不是JSON类型