一、前言

相信做过开发的同学,都多多少少写过下面的代码,很长一段时间我一直以为这便是单元测验…

@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 注解:

如何使用 Junit + Mockito 实践单元测试

以下是一些常用的 Assert 断语:

如何使用 Junit + Mockito 实践单元测试

三、Mockito 结构

从上面的介绍咱们能够认识到,怎么减少对外部的依靠才是实践单元测验的要害。而这正是Mockito的使命,Mockito 是一个流行的 mock 结构,能够与 JUnit 结合运用,Mockito 答应咱们创立和配置 mock 方针,运用 Mockito 将大大简化了具有外部依靠项的类的测验开发。spring-boot-starter-test 中默认集成了 Mockito,不需求额定引进。

在测验中运用 Mockito,一般会:

  • mock 外部依靠关系并将 mock 方针插入待测代码

  • 履行被测代码

  • 验证代码是否正确履行

如何使用 Junit + Mockito 实践单元测试

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(""));
    }
}