Github地址html
前面一个部分讲解了如何使用Spring Testing工具来测试Spring项目,如今咱们讲解如何使用Spring Boot Testing工具来测试Spring Boot项目。java
在Spring Boot项目里既可使用Spring Boot Testing工具,也可使用Spring Testing工具。
在Spring项目里,通常使用Spring Testing工具,虽然理论上也可使用Spring Boot Testing,不过由于Spring Boot Testing工具会引入Spring Boot的一些特性好比AutoConfiguration,这可能会给你的测试带来一些奇怪的问题,因此通常不推荐这样作。git
使用Spring Boot Testing工具只须要将@ContextConfiguration
改为@SpringBootTest
便可,源代码见FooServiceImpltest:github
@SpringBootTest(classes = FooServiceImpl.class) public class FooServiceImplTest extends AbstractTestNGSpringContextTests { @Autowired private FooService foo; @Test public void testPlusCount() throws Exception { assertEquals(foo.getCount(), 0); foo.plusCount(); assertEquals(foo.getCount(), 1); } }
源代码见FooServiceImpltest:spring
@SpringBootTest public class FooServiceImplTest extends AbstractTestNGSpringContextTests { @Autowired private FooService foo; @Test public void testPlusCount() throws Exception { assertEquals(foo.getCount(), 0); foo.plusCount(); assertEquals(foo.getCount(), 1); } @Configuration @Import(FooServiceImpl.class) static class Config { } }
Config:数据库
@Configuration @Import(FooServiceImpl.class) public class Config { }
@SpringBootTest(classes = Config.class) public class FooServiceImplTest extends AbstractTestNGSpringContextTests { @Autowired private FooService foo; @Test public void testPlusCount() throws Exception { assertEquals(foo.getCount(), 0); foo.plusCount(); assertEquals(foo.getCount(), 1); } }
这个例子和例子2差很少,只不过将@Configuration放到了外部。缓存
前面的例子@SpringBootTest
的用法和@ContextConfiguration
差很少。不过根据@SpringBootTest
的文档:springboot
它会尝试加载@SpringBootTest(classes=...)
的定义的Annotated classes。Annotated classes的定义在ContextConfiguration中有说明。spring-boot
若是没有设定@SpringBootTest(classes=...)
,那么会去找当前测试类的nested @Configuration class
若是上一步找到,则会尝试查找@SpringBootConfiguration
,查找的路径有:1)看当前测试类是否@SpringBootConfiguration
,2)在当前测试类所在的package里找。
因此咱们能够利用这个特性来进一步简化测试代码。
@SpringBootConfiguration @Import(FooServiceImpl.class) public class Config { }
@SpringBootTest public class FooServiceImplTest extends AbstractTestNGSpringContextTests { @Autowired private FooService foo; @Test public void testPlusCount() throws Exception { assertEquals(foo.getCount(), 0); foo.plusCount(); assertEquals(foo.getCount(), 1); } }
前面的例子咱们都使用@Import
来加载Bean,虽然这中方法很精确,可是在大型项目中很麻烦。
在常规的Spring Boot项目中,通常都是依靠自动扫描机制来加载Bean的,因此咱们但愿咱们的测试代码也可以利用自动扫描机制来加载Bean。
@SpringBootConfiguration @ComponentScan(basePackages = "me.chanjar.basic.service") public class Config { }
@SpringBootTest public class FooServiceImplTest extends AbstractTestNGSpringContextTests { @Autowired private FooService foo; @Test public void testPlusCount() throws Exception { assertEquals(foo.getCount(), 0); foo.plusCount(); assertEquals(foo.getCount(), 1); } }
也能够在测试代码上使用@SpringBootApplication
,它有这么几个好处:
自身SpringBootConfiguration
提供了@ComponentScan
配置,以及默认的excludeFilter,有了这些filter Spring在初始化ApplicationContext的时候会排除掉某些Bean和@Configuration
启用了EnableAutoConfiguration
,这个特性可以利用Spring Boot来自动化配置所须要的外部资源,好比数据库、JMS什么的,这在集成测试的时候很是有用。
@SpringBootApplication(scanBasePackages = "me.chanjar.basic.service") public class Config { }
@SpringBootTest public class FooServiceImplTest extends AbstractTestNGSpringContextTests { @Autowired private FooService foo; @Test public void testPlusCount() throws Exception { assertEquals(foo.getCount(), 0); foo.plusCount(); assertEquals(foo.getCount(), 1); } }
当@SpringBootTest
没有定义(classes=...
,且没有找到nested @Configuration class的状况下,会尝试查询@SpringBootConfiguration
,若是找到多个的话则会抛出异常:
Caused by: java.lang.IllegalStateException: Found multiple @SpringBootConfiguration annotated classes [Generic bean: class [...]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [/Users/qianjia/workspace-os/spring-test-examples/basic/target/test-classes/me/chanjar/basic/springboot/ex7/FooServiceImplTest1.class], Generic bean: class [me.chanjar.basic.springboot.ex7.FooServiceImplTest2]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [...]]
好比如下代码就会形成这个问题:
@SpringBootApplication(scanBasePackages = "me.chanjar.basic.service") public class Config1 { } @SpringBootApplication(scanBasePackages = "me.chanjar.basic.service") public class Config2 { } @SpringBootTest public class FooServiceImplTest extends AbstractTestNGSpringContextTests { // ... }
解决这个问题的方法有就是避免自动查询@SpringBootConfiguration
:
定义@SpringBootTest(classes=...)
提供nested @Configuration class
除了单元测试(不须要初始化ApplicationContext的测试)外,尽可能将测试配置和生产配置保持一致。好比若是生产配置里启用了AutoConfiguration,那么测试配置也应该启用。由于只有这样才可以在测试环境下发现生产环境的问题,也避免出现一些由于配置不一样致使的奇怪问题。
在测试代码之间尽可能作到配置共用,这么作的优势有3个:
可以有效利用Spring TestContext Framework的缓存机制,ApplicationContext只会建立一次,后面的测试会直接用已建立的那个,加快测试代码运行速度。
当项目中的Bean不少的时候,这么作可以下降测试代码复杂度,想一想若是每一个测试代码都有一套本身的@Configuration或其变体,那得多吓人。