Spring 应用进行Mockito 单元测试详解


个人理解

通过mockito给程序设定一个预期值,然后通过mockito仿真执行程序,看执行逻辑输出是否符合预期的结果。主要用于检测逻辑是否正确。由于不是真的执行,因此会隔离真实环境。无法测试底层调用或者sql是否存在问题。

mockito 资源

官网: http://mockito.org
API文档:http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html
项目源码:https://github.com/mockito/mockito

依赖


    junit
    junit
    4.12
    test


    org.mockito
    mockito-all
    1.10.19
    test


    org.mockito
    mockito-core
    3.3.3
    test


    org.powermock
    powermock-module-junit4
    1.6.5
    test


    org.powermock
    powermock-api-mockito
    1.6.5
    test

注解

注解 作用 例子
@PowerMockIgnore 忽略一些模块 @PowerMockIgnore("javax.management.*")
@PrepareForTest mock静态类 @PrepareForTest({NumberUtils.class})
@RunWith 启动注解,使用什么来运行程序 @RunWith(MockitoJUnitRunner.class)

注意事项

  • @PowerMockIgnore("javax.management.*")

由于PowerMock的工做原理便是使用自定义的类加载器来加载被修改过的类,从而达到打桩的目的,使用Powermock后会提示classloader错误,所以待测试类中使用到了XML解析相关的包和类,那么测试类前一样须要增长@PowerMockIgnore({"org.xml.", "javax.xml."}),消除类加载器引入报错。

  • @PrepareForTest({NumberUtils.class})

把静态方法mock掉,模拟调用静态方法,返回一个给定的值。

PowerMockito.mockStatic(NumberUtils.class);
when(NumberUtils.change()).thenReturn("123");
  • 调用无返回的方法
    PowerMockito.doNothing().when(casService).addSupplier(anyLong(), any(ServiceKey.class));

连续调用

@Test(expected = RuntimeException.class)
public void continuousCallTest() {
    // 模拟连续调用返回指望值,若是分开,则只有最后一个有效
    PowerMockito.when(exampleServiceMock.aTest()).thenReturn("1");
    PowerMockito.when(exampleServiceMock.aTest()).thenReturn("2");
    PowerMockito.when(exampleServiceMock.aTest()).thenReturn("3");
    PowerMockito.when(exampleServiceMock.aTest()).thenReturn("4");
    PowerMockito.when(exampleServiceMock.aTest()).thenReturn("1").thenReturn("2").thenThrow(new RuntimeException());
    Assert.assertEquals("1", exampleServiceMock.aTest());
    Assert.assertEquals("2", exampleServiceMock.aTest());
    Assert.assertEquals("3", exampleServiceMock.aTest());
    Assert.assertEquals("4", exampleServiceMock.aTest());
    // 第三次或更多调用都会抛出异常
    exampleServiceMock.aTest();
}

ExampleServiceImplTest

import com.jd.work.example.service.ExampleService;
import com.jd.work.example.utils.RedisCache;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.runners.MockitoJUnitRunner;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;

@Slf4j
@RunWith(MockitoJUnitRunner.class)
//@RunWith(PowerMockRunner.class)
@PrepareForTest()
@PowerMockIgnore("javax.management.*")
public class ExampleServiceImplTest {

    /**
     * 待测试的具体实现类
     */
    @InjectMocks
    private ExampleServiceImpl exampleService;

    @Mock
    private RedisCache redisCache;

    /**
     * 调用了自身接口的其他方法
     */
    @Mock
    private ExampleService exampleServiceMock;

    @Before
    public void setUp() {
        // mock注解初始化,不加会报错
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void example() {
        PowerMockito.when(exampleServiceMock.bTest()).thenReturn("ok-b");
        String s = exampleService.aTest();
        Assert.assertEquals("ok-b", s);
    }

   @Test(expected = RuntimeException.class)
    public void continuousCallTest() {
        // 模拟连续调用返回指望值,若是分开,则只有最后一个有效
        PowerMockito.when(exampleServiceMock.aTest()).thenReturn("1");
        PowerMockito.when(exampleServiceMock.aTest()).thenReturn("2");
        PowerMockito.when(exampleServiceMock.aTest()).thenReturn("3");
        PowerMockito.when(exampleServiceMock.aTest()).thenReturn("4");
        PowerMockito.when(exampleServiceMock.aTest()).thenReturn("1").thenReturn("2").thenThrow(new RuntimeException());
        Assert.assertEquals("1", exampleServiceMock.aTest());
        Assert.assertEquals("2", exampleServiceMock.aTest());
        Assert.assertEquals("3", exampleServiceMock.aTest());
        Assert.assertEquals("4", exampleServiceMock.aTest());
        // 第三次或更多调用都会抛出异常
        exampleServiceMock.aTest();
    }

    @Mock
    private List list;

    @Test
    public void test() {
        list.add("test");
        verify(list).add("test");
        verify(list, atLeastOnce()).add("test");
        verify(list, atLeast(1)).add("test");
        verify(list, atMost(2)).add("test");
        verify(list, never()).add("test111");
        assertThat(0, equalTo(list.size()));
    }

    @Test
    public void test3() {
        list.add("test");
        verify(list).add("test");
        // open will fail
        // list.clear();
        // 代表上一次verify之后再无与list的交互
        verifyNoMoreInteractions(list);
    }

    @Test
    public void test4() {
        list.add("test");
        // 自始至终都与list无任何交互
        verifyZeroInteractions(list);
    }
}