单元测试Junit4+mockito+powermock

2023-02-09  本文已影响0人  ybyao

1.单元测试的意义

不容易编写测试代码的代码很可能就是设计上有缺陷或者不完美,基于测试驱动理念,能写出更加符合设计更加优美的代码

1.1 常见单元测试框架

easymock, testNg, mockito

1.2 单元测试的一些原则

2.Junit4使用

简介:单元测试框架,内部涵盖了单元测试的主要流程,为单元测试提供了模板

image.png

@BeforeClass @AfterClass类级别,被标注该注解的方法只会执行一次,方法必须为public static void
@Before @After 会多次执行,每个test方法执行时都会先执行@Before,执行完test方法之后再执行@After。

单元测试启动方式

2.2 断言(https://hamcrest.org/JavaHamcrest/tutorial)

前期Junit4使用的是自己包内的断言(扩展性不好,无法动态扩充新的断言语句),后来转为使用hamcrest中的断言(org.hamcrest.MatcherAssert)。hamcrest的核心是引入了Matcher机制,核心类org.hamcrest.CoreMatchers和org.hamcrest.Matchers(hamcrest-library包中),另外可以自定义Matcher。
可以分为几大类

2.3 Runner

@RunWith
@Ignore
@Rule

//同时执行多个测试类
@RunWith(Suite.class)
@Suite.SuiteClasses({
        ParameterTest.class,
        AccountLoginControllerTest.class
})
public class MultipleTestClasses {
}

2.4 Matcher机制(hamcrest)

2.5 Rule,不经常使用

常用场景:类似Before,After等功能,灵活增加或者修改类中测试方法的行为。eg.在多个测试类中都需要连接,关闭数据库;日志输出;变相支持多个RunWith (https://github.com/junit-team/junit4/wiki/Rules)

pom.xml包依赖

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-library</artifactId>
            <version>1.3</version>
            <scope>test</scope>
        </dependency>

3.Mockito

重要的思想:理解mock/stub的基本理念,具体要解决的问题。使用最小化的资源验证代码逻辑是否符合预期,减少对数据库的连接,减少对配置文件的读写,摆脱启动时对框架的依赖。

底层原理简单介绍:
关注点:org.mockito.Mockito 该类包含了大量经常使用的函数, org.mockito.ArgumentMatchers该类抽象出来方法的各种参数

3.1框架启动常见的几种方式

1第一种 @RunWith(MockitoJUnitRunner.class)
2第二种 
    @Before
    public void init(){
        MockitoAnnotations.initMocks(this);
    }

3.2常用注解和函数

@Mock(answer = Answers.RETURNS_SMART_NULLS)
when().thenReturn()
when().thenThrow()
when().thenAnswer()
when().thenCallRealMethod()

3.4 spy

会真实执行

        List<String> realList = new ArrayList<>();
        List<String > list = spy(realList);
        list.add("Mockito");
        assertThat(list.get(0), equalTo("Mockito"));

3.5 verify

模拟验证方法是否执行过以及执行过的次数

        doNothing().when(list).clear();
        list.clear();
        verify(list, times(1)).clear();

3.6 answer

如果在方法调用中需要固定的返回值,则应使用thenReturn(…)。 如果需要执行某些操作或需要在运行时计算值,则应使用thenAnswer(…)

    private List<String> list;
    @Before
    public void init(){
        this.list = mock(ArrayList.class);
    }
    @Test
    public void stubbingWithAnswer(){
        when(list.get(anyInt())).thenAnswer(invocationOnMock ->{
           Integer index = invocationOnMock.getArgument(0);
           return String.valueOf(index * 10);
        });
        assertThat(list.get(0), equalTo("0"));
        assertThat(list.get(999), equalTo("9990"));
    }

3.7 pom.xml包依赖

<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>3.8.25</version>
    <scope>test</scope>
</dependency>

4.Powermock的功能

概念:扩展Mockito的功能,增加mock final, static, 局部变量和private方法
关注点:org.powermock.api.mockito.PowerMockito类
常用的方法:
mockStatic模拟静态方法
whenNew 模拟局部变量时可以使用

注解

@RunWith(PowerMockRunner.class)
@PrepareForTest()

4.1 常用示例

4.1.1 mock局部变量

UserService类中测试queryUserCount方法
 public int queryUserCount(){
        UserDao userDao = new UserDao();
        int remainCnt = 10; //虚拟额外的逻辑
        return userDao.getCount() + remainCnt;
    }

核心测试代码如下:

            UserDao userDao = PowerMockito.mock(UserDao.class);
            PowerMockito.whenNew(UserDao.class).withNoArguments().thenReturn(userDao);
            PowerMockito.when(userDao.getCount()).thenReturn(10);
            UserService service = new UserService();
            int result = service.queryUserCount();
            assertEquals(20, result);

其中PowerMockito.whenNew(UserDao.class).withNoArguments().thenReturn(userDao)会以局部变量对象注入到queryUserCount()方法中,另外这里的userDao是mock出来的,不能用new的方法创建。

4.1.2 模拟静态方法

    public int queryUserCount(){
        return UserDao.getCount(); // 调用的静态getCount方法
    }

核心测试代码如下:

       mockStatic(UserDao.class);
       when(UserDao.getCount()).thenReturn(10);

注意mockStatic传入的是.class对应调用的方法是PowerMockito.mockStatic(Class<?> type, Class<?>... types)

4.1.3final修饰的class

final public class UserDao{}

使用mockito模拟静态类

    @Mock
    UserDao userDao;
    @Test
    public void testQueryUserCountWithMockito() throws Exception {
        MockitoAnnotations.initMocks(this);
        when(userDao.getCount()).thenReturn(10);
        UserService userService = new UserService(userDao);
        int result = userService.queryUserCount();
        assertThat(10, equalTo(result));
    }

会出现

org.mockito.exceptions.base.MockitoException:
Mockito cannot mock/spy because :
 - final class

修改

@RunWith(PowerMockRunner.class)
@PrepareForTest({UserService.class, UserDao.class})

pom.xml中包依赖

        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-api-mockito2</artifactId>
            <version>${powermock-version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-module-junit4</artifactId>
            <version>${powermock-version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-core</artifactId>
            <version>${powermock-version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-module-junit4-rule</artifactId>
            <version>${powermock-version}</version>
            <scope>test</scope>
        </dependency>

参考链接:

代码地址(github) https://github.com/ybyao07/learnJavaUTest.git
junit4官网 https://junit.org/junit4/
mockito官网 https://site.mockito.org/
powermock 官网 https://powermock.github.io/
junit4内部依赖(hamcrest) https://hamcrest.org/
mockito内部依赖包 http://objenesis.org
学习网站:https://www.baeldung.com/
教程:https://www.tutorialspoint.com/mockito/mockito_quick_guide.htm

单元测试体系图

junit4需要了解的知识


image.png

mockito,powermock需要了解的知识


image.png
后续

2017年junit5的出现将单元测试框架整个改版,后续发展方向应该是Junit5 + Mockito4(涵盖了mockito-line,可以模拟静态,final等),powermock将会退出历史舞台(3年没人维护了,还有421个issues和17个Pull requests)。
behavior testing(行为测试): concordion, cucumber

上一篇下一篇

猜你喜欢

热点阅读