ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Mockito记录和静态方法的模拟

2021-03-29 16:34:01  阅读:173  来源: 互联网

标签:Mockito 静态方法 mockito Mock org import firstEntity public 模拟


单元测试那些坑

1. 前言

为了提高白盒测试覆盖率,项目中需要添加单元测试代码,写单元测试中很多都是用的Mock+Junit,但是我这个项目中使用的是Mock+testng,不过这两种方式我都会介绍。

2. Mock+TestNG单元测试

2.1 前提准备

这里提供一份依赖jar包的pom文件:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-all</artifactId>
    <version>1.10.19</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.testng</groupId>
    <artifactId>testng</artifactId>
    <version>7.0.0</version>
    <scope>test</scope>
</dependency>
<!--静态类方法模拟-->
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-core</artifactId>
    <version>2.0.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito</artifactId>
    <version>1.7.4</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-testng</artifactId>
    <version>1.7.4</version>
    <scope>test</scope>
</dependency>

2.2 接口方法测试

在接口中我们肯定会有一些自动注入(@Autowired)的Dao层对象或者其他对象,在Mock中有两种方式表示这些自动注入的对象

2.2.1 使用注解自动初始化

自动化注解需要使用@InjectMocks@Mock搭配使用,这样就可以在测试类运行的时候Mock这些自动注入对象,之后在@BeforeTest 中使用MockitoAnnotations.initMocks(this); 就可以了。

实例代码:

import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;

import com.xing.springDataJpa.first.FirstEntity;
import com.xing.springDataJpa.first.FirstRepository;
import com.xing.springDataJpa.service.impl.IndexServiceImpl;

import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.http.ResponseEntity;
import org.testng.Assert;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

/**
 * 功能描述
 *
 * @since 2020-09-28
 */
public class IndexServiceImplTest {
    @Mock
    private FirstRepository firstRepository;

    // @InjectMocks: 创建一个实例,简单的说是这个Mock可以调用真实代码的方法,其余用@Mock(或@Spy)注解创建的mock将被注入到用该实例中。
    @InjectMocks
    private IndexServiceImpl service;
    
    @BeforeTest
    public void beforeTest() {
        System.out.println("========= beforeTest() ==========");
        // 其中this就是单元测试所在的类,在initMocks函数中Mockito会根据类中不同的注解(如@Mock, @Spy等)创建不同的mock对象,即初始化工作
        MockitoAnnotations.initMocks(this);
    }
    
    @Test
    public void getPerson() {
        String userId = "3423423113";
        String userName = "xiaoming";
        FirstEntity firstEntity = new FirstEntity();
        firstEntity.setUserId(userId);
        firstEntity.setUserName(userName);
        when(firstRepository.findFirstByUserIdAndAndUserName(anyString(), anyString()))
                .thenReturn(firstEntity);
        ResponseEntity<Object> person = service.getPerson(userId, userName);
        Assert.assertNotNull(person);
    }
    
}

2.2.2 使用反射机制初始化注入对象

这种是很简便的使用,如果想多写几行代码可以使用反射机制,set这些对象。

import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;

import com.xing.springDataJpa.first.FirstEntity;
import com.xing.springDataJpa.first.FirstRepository;
import com.xing.springDataJpa.service.impl.IndexServiceImpl;

import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.http.ResponseEntity;
import org.testng.Assert;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import java.lang.reflect.Field;

/**
 * 功能描述
 *
 * @since 2020-09-28
 */
public class IndexServiceImplTest {
    IndexServiceImpl service = new IndexServiceImpl();
    
    @Mock
    private FirstRepository firstRepository;
    
    @BeforeTest
    public void beforeTest() throws NoSuchFieldException, IllegalAccessException {
        System.out.println("========= beforeTest() ==========");
        // 其中this就是单元测试所在的类,在initMocks函数中Mockito会根据类中不同的注解(如@Mock, @Spy等)创建不同的mock对象,即初始化工作
        MockitoAnnotations.initMocks(this);
        
        Class serviceClass = service.getClass();
        // 反射获取属性
        Field firstRepositoryField = serviceClass.getDeclaredField("firstRepository");
        firstRepositoryField.setAccessible(true);
        firstRepositoryField.set(service, firstRepository);
    }
    
    @Test
    public void getPerson() {
        String userId = "3423423113";
        String userName = "xiaoming";
        FirstEntity firstEntity = new FirstEntity();
        firstEntity.setUserId(userId);
        firstEntity.setUserName(userName);
        when(firstRepository.findFirstByUserIdAndAndUserName(anyString(), anyString()))
                .thenReturn(firstEntity);
        ResponseEntity<Object> person = service.getPerson(userId, userName);
        Assert.assertNotNull(person);
    }
    
}

2.3 静态类方法测试

在复杂的接口业务中,经常会用到一些工具类,静态方法等等,这类单元测试需要通过PowerMockito来实现静态方法的模拟。

静态方法:

public class StaticUtils {
    
    public static String printList(List<String> list) {
            System.out.println("============ StaticUtils.printHello()============");
            list.forEach(System.out::println);
            return "printList";
        }
}

接口类添加静态方法:

@Override
    public ResponseEntity<Object> getPerson(String userId, String userName) {
        String s = StaticUtils.printList(Arrays.asList("a", "b", "c"));
        FirstEntity firstEntity = firstRepository.findFirstByUserIdAndAndUserName(userId,userName);
        return ResponseEntity.ok().body(firstEntity);
    }

PowerMockito.mockStatic(StaticUtils.class); 要放在 MockitoAnnotations.initMocks(this); 前面才可以,示例代码:

import static org.mockito.Matchers.anyListOf;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;

import com.xing.springDataJpa.first.FirstEntity;
import com.xing.springDataJpa.first.FirstRepository;
import com.xing.springDataJpa.service.impl.IndexServiceImpl;
import com.xing.springDataJpa.utils.StaticUtils;

import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.testng.PowerMockObjectFactory;
import org.springframework.http.ResponseEntity;
import org.testng.Assert;
import org.testng.IObjectFactory;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.ObjectFactory;
import org.testng.annotations.Test;

/**
 * 功能描述
 *
 * @since 2020-09-28
 */
@PrepareForTest(StaticUtils.class)
public class IndexServiceImplTest  {
    @Mock
    private FirstRepository firstRepository;

    // @InjectMocks: 创建一个实例,简单的说是这个Mock可以调用真实代码的方法,其余用@Mock(或@Spy)注解创建的mock将被注入到用该实例中。
    @InjectMocks
    private IndexServiceImpl service;
    
    @ObjectFactory
    public IObjectFactory getObjectFactory() {
        return new PowerMockObjectFactory();
    }
    
    @BeforeTest
    public void beforeTest() throws NoSuchFieldException, IllegalAccessException {
        System.out.println("========= beforeTest() ==========");
        // 其中this就是单元测试所在的类,在initMocks函数中Mockito会根据类中不同的注解(如@Mock, @Spy等)创建不同的mock对象,即初始化工作
        PowerMockito.mockStatic(StaticUtils.class);
        MockitoAnnotations.initMocks(this);
    }
    
    @Test
    public void getPerson() {
        String userId = "3423423113";
        String userName = "xiaoming";
        FirstEntity firstEntity = new FirstEntity();
        firstEntity.setUserId(userId);
        firstEntity.setUserName(userName);
        when(firstRepository.findFirstByUserIdAndAndUserName(anyString(), anyString()))
                .thenReturn(firstEntity);
        // 模拟静态方法
        String rel = "rel";
        PowerMockito.when(StaticUtils.printList(anyListOf(String.class)))
                .thenReturn(rel);
        ResponseEntity<Object> person = service.getPerson(userId, userName);
        Assert.assertNotNull(person);
    }
    
}

3. Mock+Junit单元测试

完整的代码:

import static org.mockito.Matchers.anyListOf;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;

import com.xing.springDataJpa.first.FirstEntity;
import com.xing.springDataJpa.first.FirstRepository;
import com.xing.springDataJpa.service.impl.IndexServiceImpl;
import com.xing.springDataJpa.utils.StaticUtils;

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.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.http.ResponseEntity;


/**
 * 功能描述
 *
 * @since 2020-09-28
 */

@RunWith(PowerMockRunner.class)
@PrepareForTest(StaticUtils.class)
public class IndexServiceImplTest  {
    @Mock
    private FirstRepository firstRepository;

    // @InjectMocks: 创建一个实例,简单的说是这个Mock可以调用真实代码的方法,其余用@Mock(或@Spy)注解创建的mock将被注入到用该实例中。
    @InjectMocks
    private IndexServiceImpl service;
    
    @Before
    public void beforeTest() throws NoSuchFieldException, IllegalAccessException {
        System.out.println("========= beforeTest() ==========");
        // 其中this就是单元测试所在的类,在initMocks函数中Mockito会根据类中不同的注解(如@Mock, @Spy等)创建不同的mock对象,即初始化工作
        PowerMockito.mockStatic(StaticUtils.class);
        MockitoAnnotations.initMocks(this);
    }
    
    @Test
    public void getPerson() {
        String userId = "3423423113";
        String userName = "xiaoming";
        FirstEntity firstEntity = new FirstEntity();
        firstEntity.setUserId(userId);
        firstEntity.setUserName(userName);
        when(firstRepository.findFirstByUserIdAndAndUserName(anyString(), anyString()))
                .thenReturn(firstEntity);
        // 模拟静态方法
        String rel = "rel";
        PowerMockito.when(StaticUtils.printList(anyListOf(String.class)))
                .thenReturn(rel);
        ResponseEntity<Object> person = service.getPerson(userId, userName);
        Assert.assertNotNull(person);
    }
    
}

相比较TestNG 只有部分代码不一样其他都差不多。

4. 注解说明

这里是TestNG的一些注解说明:

@BeforeClass---@AfterClass

类实例化前, 被执行, 主要用于设置环境变量等, 与SpringTestContext结合用的时候要注意, 这种情况下@autowire的bean还未实例化

@BeforeTest----@AfterTest

整个测试类开始前, 被执行, 主要用户塞值, 或者进行mock(Object)的初始化, 此方法只会运行一次

@BeforeMethod-----@AfterMethod

每个测试方法执行前, 进行调用, 和@BeforeTest的主要区别在于, 你如果需要每次清空你测试的一些上下文, 那么需要配合@AfterMethod一起使用

标签:Mockito,静态方法,mockito,Mock,org,import,firstEntity,public,模拟
来源: https://blog.csdn.net/sdut406/article/details/115304272

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有