美文网首页
单元测试Junit4+mockito+powermock

单元测试Junit4+mockito+powermock

作者: ybyao | 来源:发表于2023-02-09 18:54 被阅读0次

    1.单元测试的意义

    • 梳理思维逻辑
    • 提升编码能力,架构能力和代码质量
    • 基本思想:输入输出,期望值和真实值对比

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

    1.1 常见单元测试框架

    easymock, testNg, mockito

    1.2 单元测试的一些原则

    • 一次只关注一个功能函数。开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为
    • 单元测试覆盖功能范围。主体功能,边界条件,特殊情况的处理
    • 单元测试覆盖率,不追求100%。语句覆盖,分支覆盖,条件覆盖,分支-条件覆盖,条件组合覆盖,路径覆盖
    • 需要练习才能掌握

    2.Junit4使用

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

    image.png

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

    单元测试启动方式

    • IDEA开发平台自带的Run工具(最常用)
    • 使用单元测试类JUnitCore.runClasses(研究源码的可以通过此类作为入口)
    • mvn命令执行时test阶段

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

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

    • 一般匹配断言
      1. assertThat(“myValue”, allOf(startsWith(“my”), containsString(“Val”)))
      2. assertThat( “myValue”, anything() )
      3. assertThat( “myValue”, not(“foo”))
    • 字符串匹配
      1. assertThat(“myStringOfNote”, containsString(“ring”))
      2. assertThat(“myStringOfNote”, startsWith(“my”))
    • 数值相关匹配
      1.assertThat(2, greaterThan(1))
      2.assertThat(1.03, is(closeTo(1.0, 0.03)))
    • 集合相关匹配
      1.assertThat(myMap, hasEntry(“bar”, “foo”))
      2.assertThat(Arrays.asList(“foo”, “bar”), hasItem(startsWith(“ba”)))
      3.assertThat(myMap, hasKey(“bar”))
    • Beans相关
      1.hasProperty
      2.samePropertyValuesAs
      3.getPropertyDescriptor

    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

    相关文章

      网友评论

          本文标题:单元测试Junit4+mockito+powermock

          本文链接:https://www.haomeiwen.com/subject/iwwckdtx.html