(001)springboot中测试的基础知识以及接口和Controller的测试
(一)springboot中测试的基础知识
(1)添加starter-test依赖,范围指定为test,只在执行测试时生效
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
完整pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.edu.spring</groupId> <artifactId>springboot_web</artifactId> <version>1.0.0</version> <name>springboot_web</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.4.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> </project>
(2)新建类UserDao.java,并添加测试类
package com.edu.spring.springboot.dao; import org.springframework.stereotype.Repository; @Repository public class UserDao { public Integer addUser(String username){ System.out.println("user dao addUser["+username+"]"); if(username==null){ return 0; } return 1; } }
选中要测试的类 -> 右键 -> new -> 输入ju,选择JUnit Test Case -> next -> 选择填写正确信息 -> next -> 选择测试的方法 -> finish
UserDaoTest.java,测试类需要添加注解@RunWith(SpringRunner.class)、@SpringBootTest
package com.edu.spring.springboot.dao; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class UserDaoTest { @Autowired private UserDao userDao; @Test public void testAddUser() { Assert.assertEquals(Integer.valueOf(1), userDao.addUser("root")); Assert.assertEquals(Integer.valueOf(0), userDao.addUser(null)); } }
运行如下:
(3)在测试环境创建bean需要用@TestConfiguration,并且该注解只在测试环境下有效
TestBeanConfiguration.java(src/test/java),创建Runnable类型的bean
package com.edu.spring.springboot.dao; import org.springframework.context.annotation.Bean; import org.springframework.boot.test.context.TestConfiguration; @TestConfiguration public class TestBeanConfiguration { @Bean public Runnable createRunnable(){ return () -> {}; } }
ApplicationContextTest.java(src/test/java),指定测试环境的配置类 @SpringBootTest(classes=TestBeanConfiguration.class)
package com.edu.spring.springboot.dao; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.util.Assert; @RunWith(SpringRunner.class) @SpringBootTest(classes=TestBeanConfiguration.class) public class ApplicationContextTest { @Autowired private ApplicationContext context; @Test public void testNull(){ Assert.notNull(context.getBean(Runnable.class),"获取Runnable.class的bean出错"); } }
运行结果如下:
不在测试环境无法获取@TestConfiguration中创建的bean,如下图
(4)springboot会优先加载测试环境下的application.properties,测试环境下没有才会加载正常环境下的
application.properties(src/test/resources)
app.name=springboottest
EnvTest.java(src/test/java),测试环境使用ConfigurableEnvironment获取配置文件属性
package com.edu.spring.springboot.dao; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class EnvTest { @Autowired private ConfigurableEnvironment env; @Test public void testValue(){ Assert.assertEquals("springboottest",env.getProperty("app.name")); } }
运行结果如下:
删除测试环境的application.properties,在正常环境的application.properties添加如下配置
app.name=springboot
EnvTest.java修改为:
Assert.assertEquals("springboot",env.getProperty("app.name"));
运行结果如下:
(5)运行时指定配置@SpringBootTest(properties = {"app.admin.name=zhangsan","app.admin.passwd=123456"})
EnvTest.java
package com.edu.spring.springboot.dao; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(properties = {"app.admin.name=zhangsan","app.admin.passwd=123456"}) public class EnvTest { @Autowired private ConfigurableEnvironment env; @Test public void testValue(){ Assert.assertEquals("zhangsan",env.getProperty("app.admin.name")); Assert.assertEquals("123456",env.getProperty("app.admin.passwd")); } }
运行结果如下:
(二)springboot中测试接口,使用注解@MockBean 和 BDDMockito类
接口UserMapper.java
package com.edu.spring.springboot.mapper; public interface UserMapper { public Integer addUser(String username); }
测试类UserMapperTest.java,@MockBean注入接口,BDDMockito给出预测
package com.edu.spring.springboot.mapper; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.BDDMockito; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class UserMapperTest { @MockBean private UserMapper userMapper; @Test(expected=NullPointerException.class) public void createUserTest(){ BDDMockito.given(userMapper.addUser("admin")).willReturn(Integer.valueOf(1)); BDDMockito.given(userMapper.addUser("")).willReturn(Integer.valueOf(0)); BDDMockito.given(userMapper.addUser(null)).willThrow(NullPointerException.class); Assert.assertEquals(Integer.valueOf(1), userMapper.addUser("admin")); Assert.assertEquals(Integer.valueOf(0), userMapper.addUser("")); Assert.assertEquals(Integer.valueOf(0), userMapper.addUser(null)); } }
或者在初始化方法中给出预测:
package com.edu.spring.springboot.mapper; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.BDDMockito; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class UserMapperTest { @MockBean private UserMapper userMapper; @Before public void init(){ BDDMockito.given(userMapper.addUser("admin")).willReturn(Integer.valueOf(1)); BDDMockito.given(userMapper.addUser("")).willReturn(Integer.valueOf(0)); BDDMockito.given(userMapper.addUser(null)).willThrow(NullPointerException.class); } @Test(expected=NullPointerException.class) public void createUserTest(){ Assert.assertEquals(Integer.valueOf(1), userMapper.addUser("admin")); Assert.assertEquals(Integer.valueOf(0), userMapper.addUser("")); Assert.assertEquals(Integer.valueOf(0), userMapper.addUser(null)); } }
运行结果如下:
(三)springboot中测试Controller的两种方式:TestRestTemplate 和 MockMvc,这里从构建测试环境分3种进行说明
BookController.java
package com.edu.spring.springboot; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class BookController { @GetMapping("/book/home") public String home(){ return "book home"; } @GetMapping("/book/show") public String show(String id){ return "book"+id; } }
(1)@SpringBootTest注解,指定webEnvironment 和 注入TestRestTemplate
BookControllerTest.java
package com.edu.spring.springboot; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT) public class BookControllerTest { @Autowired private TestRestTemplate restTemplate; @Test public void testHome() { String actual=restTemplate.getForObject("/book/home",String.class); Assert.assertEquals("book home", actual); } @Test public void testShow() { String actual=restTemplate.getForObject("/book/show?id=100",String.class); Assert.assertEquals("book100", actual); } }
运行结果如下:
(2)@WebMvcTest注解,指定controllers 和 注入MockMvc
BookControllerTest2.java
package com.edu.spring.springboot; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; @RunWith(SpringRunner.class) @WebMvcTest(controllers=BookController.class) public class BookControllerTest2 { @Autowired private MockMvc mockMvc; @Test public void testHome() throws Exception { mockMvc.perform(MockMvcRequestBuilders.get("/book/home")).andExpect(MockMvcResultMatchers.status().isOk()); mockMvc.perform(MockMvcRequestBuilders.get("/book/home")).andExpect(MockMvcResultMatchers.content().string("book home")); } @Test public void testShow() throws Exception{ mockMvc.perform(MockMvcRequestBuilders.get("/book/show").param("id","100")).andExpect(MockMvcResultMatchers.status().isOk()); mockMvc.perform(MockMvcRequestBuilders.get("/book/show").param("id","100")).andExpect(MockMvcResultMatchers.content().string("book100")); } }
运行结果如下:
假如在 BookController.java 中注入 UserDao,即添加如下代码,再次测试会报错,因为@WebMvcTest不会加载整个spring容器
@Autowired UserDao userDao;
运行结果如下
(3)使用@SpringBootTest、@AutoConfigureMockMvc 注解,注入MockMvc
BookControllerTest3.java
package com.edu.spring.springboot; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; @RunWith(SpringRunner.class) @SpringBootTest @AutoConfigureMockMvc public class BookControllerTest3 { @Autowired private MockMvc mockMvc; @Test public void testHome() throws Exception { mockMvc.perform(MockMvcRequestBuilders.get("/book/home")).andExpect(MockMvcResultMatchers.status().isOk()); mockMvc.perform(MockMvcRequestBuilders.get("/book/home")).andExpect(MockMvcResultMatchers.content().string("book home")); } @Test public void testShow() throws Exception{ mockMvc.perform(MockMvcRequestBuilders.get("/book/show").param("id","100")).andExpect(MockMvcResultMatchers.status().isOk()); mockMvc.perform(MockMvcRequestBuilders.get("/book/show").param("id","100")).andExpect(MockMvcResultMatchers.content().string("book100")); } }
运行结果如下:
总结:
@SpringBootTest 会加载整个spring容器
@WebMvcTest 不需要运行在web环境下,但是需要指定controllers,这种方法只测试controller,不会加载整个spring容器
如果依然使用MockMvc,需要添加@SpringBootTest 和 @AutoConfigureMockMvc注解
@SpringBootTest 和 @WebMvcTest不能同时使用