当需要启动完整spring容器的时候考虑使用。
如果没有指定使用注解@ContextConfiguration(loader=...)指定加载,SpringBootContextLoader就是默认的加载器。当没有嵌套使用@Configuration时候,会自动搜索 @SpringBootConfiguration注解。 当我们用@SpringBootConfiguration标记一个类时,意味着该类提供了@Bean定义方法。Spring 容器为我们的应用程序实例化和配置 bean。 @SpringBootConfiguration与@Configuration区别在于@SpringBootConfiguration允许自动发现配置。
@SpringBootTest会首先在当前包中搜索@SpringBootConfiguration,如果没有搜索到则根据包结构向上搜索。
测试类需要与@SpringbootApplication注解标记的类在同一个包内,或者在更下层的包内。(与上句话一致,表达方式不同)
简单的说这个逻辑链条就是:@SpringbootTest去找@SpringbootApplication,@SpringbootApplication注解又包含了SpringBootConfiguration注解。
如果你需要一个跟src/main下代码不一样的应用配置,考虑使用自定义springbootapplication启动类,放在src/test。
@SpringBootTest(classes = CustomApplication.class) class CustomApplicationTest {
由于@SpringBootTest会完整的启动容器,建议在集成测试时考虑使用。
指定测试用例要使用的配置,如果不同的测试用例有不同的测试配置,那么可以使用这个注解把配置加载到ApplicationContext中。 例如:
@TestPropertySource("classpath:application.properties")
如果没有指定location,那么默认搜索类名关联的配置进行加载。 如果使用注解测测试类是 is com.example.MyTest, 相应的配置文件就是 "classpath:com/example/MyTest.properties". 如果没有找到默认配置,则抛出IllegalStateException异常。
加载配置类。如果使用了SpringBootTest,这个注解是不需要的。当你需要Spring容器,又不希望加载全部的类时候,可以考虑用@ContextConfiguration指定加载。某种程度上来说@SpringBootTest(classes="")与@ContextConfiguration(classes="")是等价的。
@Import与@ContextConfiguration是完全不同的使用场景。不建议互换(有时也不能互换)。 @Import用于一个配置类中,导入其他配置类。例如:
@Configuration @Import(PersistenceConfig.class) public class MainConfig {}
比如你禁用了一个包的component scan,那么但是你需要那个包中的一个配置类的时候,你可以考虑用@Import。
@ContextConfiguration只能用于spring测试。
SpringRunner是SpringJUnit4ClassRunner的别名,所以@RunWith(SpringRunner.class) @RunWith(SpringJUnit4ClassRunner)是等价的。
Junit注解参考Junit说明即可。
重点说三遍:
@RunWith(MockitoJUnitRunner.class) public class TodoListServiceTest { @InjectMocks private ToDoService toDoService; @Mock private TodoListDao mockDao; @Test public void findAllTest() throws Exception { List<ToDoList> toDoList = new ArrayList<ToDoList>(); toDoList.add(new ToDoList(1L,"jogging at 6:00",true)); toDoList.add(new ToDoList(2L,"meeting at 10:00",true)); when(mockDao.findAll()).thenReturn(toDoList); List<ToDoList> toDoList2 = toDoService.findAll(); verify(mockDao).findAll(); assertThat(toDoList2).hasSize(2); @Test public void countTest(){ when(mockDao.count()).thenReturn(2); long count = toDoService.count(); verify(mockDao).count(); assertThat(count).isEqualTo(2L);
使用mokcito时,测试用例中verify和assert应该都有。verify确保mock的方法被调用。
@RunWith(MockitoJUnitRunner.class) public class TodoListControllerStandaloneTest { @Autowired MockMvc mockMvc; @Mock private ToDoService toDoService; @InjectMocks ToDoListController toDoListController; @Before public void setup() { JacksonTester.initFields(this, new ObjectMapper()); // MockMvc standalone approach mockMvc = MockMvcBuilders.standaloneSetup(toDoListController) .build(); @Test public void getAllToDos() throws Exception { List<ToDoList> toDoList = new ArrayList<ToDoList>(); toDoList.add(new ToDoList(1L,"jogging at 6:00",true)); toDoList.add(new ToDoList(2L,"meeting at 10:00",true)); when(toDoService.findAll()).thenReturn(toDoList); mockMvc.perform(get("/todos") .contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$",hasSize(2))) .andDo(print()); verify(toDoService).findAll();
关于controller测试更多的内容可以参考:https://github.com/mechero/spring-boot-testing-strategies
在写测试用例中,mybatis的测试每次都要启动spring容器,这导致非常耗时。
@MybatisTest注解需要SqlSession和SqlFactory,在使用spring自动配置机制时候,这个由mybatis-spring提供。
在这种情形下,你必须使用spring容器加载mybatis,才可能获得session。为了不启动容器,加速测试用例的运行,建议提供freshal-cloud测试支持
public class DaoWithoutSpringTest { protected static SqlSessionFactory sqlSessionFactory; protected static SqlSession session; public static List<Class<?>> mapperfile = new ArrayList<Class<?>>(); * 返回默认的配置文件,如果文件名称不一样,则在测试用例中复写本方法。 * 仍然使用spring的配置文件,但是不启动spring容器。 * @return protected static String getPropertyFile(){ return "application.properties"; public static void setUpDatabse() throws IOException { Properties properties = PropertiesLoaderUtils.loadProperties(new ClassPathResource(getPropertyFile())); String user = properties.getProperty("spring.datasource.username"); String password = properties.getProperty("spring.datasource.password"); String url = properties.getProperty("spring.datasource.url"); String driver = properties.getProperty("spring.datasource.driverClassName"); DataSource dataSource = new org.apache.ibatis.datasource.pooled.PooledDataSource( driver, url, user, password); TransactionFactory transactionFactory = new JdbcTransactionFactory(); Environment environment = new Environment("development", transactionFactory, dataSource); Configuration configuration = new Configuration(environment); for(Class clazz: mapperfile){ configuration.addMapper(clazz); sqlSessionFactory = new SqlSessionFactoryBuilder() .build(configuration); session = sqlSessionFactory.openSession(); @AfterClass public static void close(){ session.close();
可以写dao的测试用例如下:
public class TodoDaoWithoutSpringTest extends DaoWithoutSpringTest{ @Test public void findAllTest() { //使用session获得dao TodoListDao todoListDao = session.getMapper(TodoListDao.class); List<ToDoList> list = todoListDao.findAll(); assertThat(list).hasSize(2); @BeforeClass public static void setUp() throws IOException { //添加mapper配置类 mapperfile.add(TodoListDao.class); //初始化session setUpDatabse();
无需mybatis注解,就可以测试dao。
控制台日志显示非常简短
16:16:09.960 [main] DEBUG org.apache.ibatis.logging.LogFactory - Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter. 16:16:10.172 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Opening JDBC Connection 16:16:10.451 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource - Created connection 132577100. 16:16:10.452 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7e6f74c] 16:16:10.457 [main] DEBUG com.freshal.sample.tdd.TodoListDao.findAll - ==> Preparing: select * from todos 16:16:10.511 [main] DEBUG com.freshal.sample.tdd.TodoListDao.findAll - ==> Parameters: 16:16:10.547 [main] DEBUG com.freshal.sample.tdd.TodoListDao.findAll - <== Total: 2 16:16:10.629 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7e6f74c] 16:16:10.630 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7e6f74c] 16:16:10.630 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource - Returned connection 132577100 to pool.
上面是用注解方式配置mybatis,xml需要对应修改。
https://www.appsdeveloperblog.com/the-first-principle-in-unit-testing/
我们总结一下几个关于springboot测试的实践
@SpringBootTest(classes = ABC.class)
SpringBoot 测试 SpringBoot的Contoller测试