上一篇介绍了JUNIT的基本使用,接下来就是利用JUNIT进行Springboot项目测试。Springboot一大特点就是IOC容器,所有注释@Bean的类可以自动转配到Spring-IOC容器并进行实例化以及生命周期管理。那么问题是能否可以直接在test文件夹下@Autowired src文件下的Bean类?
Springboot单元测试 - 如何载入依赖类
假设在src文件夹下构建了一下如下@Service类,我们直接在测试类中@Autowired注入这个Bean。下图是运行结果,可以看出测试类并没能成功注入Bean,也就是说测试类并没有加入Spring的应用上下文(ApplicationContext)即Spring容器中。
@Service
public class MongoService {public void output() {System.out.println("This is a bean!!");}public boolean result() {return true;}
}
public class UnitTest {@AutowiredMongoService mongoService;@Testpublic void testBean() {mongoService.output();System.out.println("Test Autowird Spring Bean");}
}============== 运行结果 ==============
java.lang.NullPointerExceptionat com.example.springweb.MongoTest.testBean
解决方法很简单,需要将当前测试类运行在Spring应用上下文环境而不是原始的JUNIT环境,并且加载包含主程序Beans的容器也就是@SpringBootApplication类。可以通过两个注解来实现,加入这两个注解后等价于运行了在测试环境运行了主程序Springboot环境
Note:
- @RunWith(SpringJUnit4ClassRunner.class) - 运行Spring环境
- @SpringApplicationConfiguration(classes=SpringbootApplication.class) - 寻找主程序容器以此加载所有Beans
/**** Springboot 1.4 之前RunWith - SpringJunit4ClassRunner** */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes=SpringwebApplication.class)
public class UnitTest {/*** cannot autowird the bean since this class is not in springboot container* */@AutowiredMongoService mongoService;@Testpublic void testBean() {mongoService.output();System.out.println("Test Autowird Spring Bean");}
}
Note:
- Springboot 1.4.x 之后SpringJUnit4ClassRunner.class 换为 SpringRunner.class
- Springboot 2.x 之后@SpringApplicationConfiguration 换为 @SpringbootTest功能更强大
这样就能够成功注入当前测试类需要的各种Bean依赖了。
Springboot单元测试 - 依赖类mock测试
通常单元测试中,我们会隔离依赖对于测试类的影响,也就是假设所有依赖的一定会输出理想结果,在测试中可以通过Mock方法来确保输出结果,这也就引入另一个测试框架Mockito。
Mockito框架的作用就是模拟接口功能,并不运行模拟接口的实际逻辑,而是直接输出一个假定结果。
Mockito常见注释及方法
@Mock | 被注释的对象会作为Mock对象 |
@InjectMocks | 被注释的对象依赖于@Mock的对象,通常是测试对象 |
when(.).thenReturn() | 对@Mock的对象进行模拟输出 |
doThrow().when(). | 对Mock的对象模拟抛出一个异常 |
spy() | 也是建立一个模拟对象,但可以 |
| |
| |
待测试类 - AController
public class AController {@AutowiredMongoService mongoService;public boolean testBean() {boolean res = mongoService.testMethod2(4);return res;}}
依赖类 - MongoService
@Service
public class MongoService {public void output() {System.out.println("This is a bean!!");}public boolean testMethod1(int num) {return num > 0;}public boolean testMethod2(int num) {int val = getVal();return val > num;}private int getVal() {return 2;}
}
Mockito测试类 - RunWith需要运行MockitoJUnitRunner.class才能使用Mockito相关注解
@RunWith(MockitoJUnitRunner.class)
public class MockTest {@MockMongoService mongoService;@InjectMocksAController aController;@Testpublic void testMock1() {when(mongoService.testMethod2(Mockito.anyInt())).thenReturn(true);Assert.assertEquals(aController.testBean(), true);}@Test(expected = Exception.class)public void testMock2() {doThrow(Exception.class).when(mongoService).testMethod2(Mockito.anyInt());aController.testBean();}
}
@Mock 和 @Spy区别
上面两种注释都是建立一个模拟对象,但是区别是@Mock注释的对象是完全虚拟,不能调用这个Mock对象的任何方法,只能通过when().thenReturn() 来进行模拟输出;但是@Spy是建立一个真实的Mock对象,可以调用对象中任何方法也可以模拟某些方法的输出。@Spy大大增加了测试的自由度,比如如果某个测试类你只想模拟某个方法但是想要某些方法的真实输出,这是@Spy就可以解决这个问题。
举例 - 模拟MongoService其中一个方法,并测试另一个方法真实输出
@SpyMongoService mongoServiceSpy;@Testpublic void testSpy() {/** 模拟MongoService中 getVal并测试testMethod2*/when(mongoServiceSpy.getVal()).thenReturn(3);Assert.assertEquals(mongoServiceSpy.testMethod2(2), true);}
Reference
- SpringBoot Test及注解详解 - codedot - 博客园
- https://www.jianshu.com/p/51930cc5dcf9