一、前言
相信做过开发的同学,都多多少少写过下面的代码,很长一段时间我一直以为这便是单元测验…
@SpringBootTest
@RunWith(SpringRunner.class)
public class UnitTest1 {
@Autowired
private UnitService unitService;
@Test
public void test() {
System.out.println("----------------------");
System.out.println(unitService.sayHello());
System.out.println("----------------------");
}
}
但这是单元测验嘛?unitService 中或许还依靠了 Dao 的操作;如果是微服务,或许还要起注册中心。那么这个“单元”也太大了吧!如果把它称为集成测验,或许更恰当一点,那么有没有或许最小粒度进行单元测验嘛?
单元测验应该是一个带有阻隔性的功用测验。在单元测验中,应尽量避免其他类或系统的副作用影响。
单元测验的方针是一小段代码,例如办法或类。办法或类的外部依靠关系应从单元测验中移除,而改为测验结构创立的 mock 方针来替换依靠方针。
单元测验一般由开发人员编写,经过验证或断语方针的一些行为或状况来到达测验的目的。
二、JUnit 结构
JUnit 是一个测验结构,它运用注解来标识测验办法。JUnit 是 Github 上保管的一个开源项目。
一个 JUnit 测验指的是一个包含在测验类中的办法,要界说某个办法为测验办法,请运用 @Test 注解标示该办法。该办法履行被测代码,能够运用 JUnit 或另一个 Assert 结构提供的 assert 办法来检查预期结果与实际结果是否共同,这些办法调用一般称为断语或断语句子。
public class UnitTest2 {
@Test
public void test() {
String sayHello = "Hello World";
Assert.assertEquals("Hello World", sayHello);
}
}
以下是一些常用的 JUnit 注解:
以下是一些常用的 Assert 断语:
三、Mockito 结构
从上面的介绍咱们能够认识到,怎么减少对外部的依靠才是实践单元测验的要害。而这正是Mockito的使命,Mockito 是一个流行的 mock 结构,能够与 JUnit 结合运用,Mockito 答应咱们创立和配置 mock 方针,运用 Mockito 将大大简化了具有外部依靠项的类的测验开发。spring-boot-starter-test 中默认集成了 Mockito,不需求额定引进。
在测验中运用 Mockito,一般会:
-
mock 外部依靠关系并将 mock 方针插入待测代码
-
履行被测代码
-
验证代码是否正确履行
3.1 运用 Mockito 创立 mock 方针
Mockito 提供了几种创立 mock 方针的办法:
- 运用静态 mock() 办法
- 运用 @Mock 注解
如果运用 @Mock 注解,则有必要触发创立带有 @Mock 注解的方针。运用 MockitoRule 能够做到,它经过调用静态办法 MockitoAnnotations.initMocks(this) 来填充带 @Mock 注解的字段。或许能够运用 @RunWith(MockitoJUnitRunner.class)。
public class UnitTest3 {
// 触发创立带有 @Mock 注解的方针
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
// 1. 运用 @Mock 注解创立 mock 方针
@Mock private UnitDao unitDao;
@Test
public void test() {
// 2. 运用静态 mock() 办法创立 mock 方针
Iterator iterator = mock(Iterator.class);
// when...thenReturn / doReturn...when 模仿依靠调用
when(iterator.next()).thenReturn("hello");
doReturn(1).when(unitDao).delete(anyLong());
// 断语
Assert.assertEquals("hello", iterator.next());
Assert.assertEquals(new Integer(1), unitDao.delete(1L));
}
}
3.2 运用 mock 方针实践单元测验
咱们要单元测验的内容,常常包含着对数据库的拜访等等,那么咱们要怎么 mock 掉这部分调用呢?咱们能够运用 @InjectMocks 注解创立实例并运用 mock 方针进行依靠注入。
@Service
public class UnitServiceImpl implements UnitService {
@Autowired
private UnitDao unitDao;
@Override
public String sayHello() {
Integer delete = unitDao.delete(1L);
System.out.println(delete);
return "hello unit";
}
}
@RunWith(MockitoJUnitRunner.class)
public class UnitTest2 {
@Mock
private UnitDao unitDao;
@InjectMocks
private UnitServiceImpl unitService;
@Test
public void unitTest() {
// mock 调用
when(unitDao.delete(anyLong())).thenReturn(1);
Assert.assertEquals("hello unit", unitService.sayHello());
}
}
Mockito 还有很多风趣的实践,比如:@Spy或spy()办法、verify()验证等等,鉴于篇幅原因,读者可自行挖掘。
3.3 运用 PowerMock mock 静态办法。
Mockito 也有一些局限性。例如:不能 mock 静态办法和私有办法。有关详细信息,请参阅Mockito约束的常见问题解答。这个时候咱们就要用到 PowerMock,PowerMock 支持 JUnit 和 TestNG,扩展了 EasyMock 和 Mockito 结构,增加了mock static、final 办法的功用。
首先需求引进 PowerMock 的依靠:
<!-- PowerMock -->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.7</version>
</dependency>
接下来就能愉快的 mock 静态办法了。
@RunWith(PowerMockRunner.class)
@PrepareForTest({StringUtils.class})
public class UnitTest4 {
@Test
public void test() {
mockStatic(StringUtils.class);
when(StringUtils.getFilename(anyString())).thenReturn("localhost");
Assert.assertEquals("localhost", StringUtils.getFilename(""));
}
}