浅谈单元测试之PowerMock

简介: PowerMock作为其他Mock框架的补充和扩展,有其自身独到的优势:final 类型的 class 和 method 的 mock 操作;完成对类方法(static)的 mock;完成对局部变量的 mock 等等。总之一句话:PowerMock 的出现为解决此类疑难问题另辟蹊径。
  1. PowerMock介绍
    Mock是在测试过程中,对于一些不容易构造/获取的对象,创建一个虚拟mock数据对象来模拟对象的行为。在现实的软件开发过程中,我们经常需要协同其他同事一起来完成某个模块的功能开发,或者需要第三方资源,比如您需要一个短信网关,您需要一个数据库,您需要一个消息中间件,当您进行测试依赖于这些资源的代码时候,不可能在每次都具备相应的环境,这将Programming 系列丛书是一个很不现实的问题,如果当您依赖于其他模块而无法进行单元测试的时候,此时该模块的质量风险就有两个,第一是您所负责的代码,第二是您所依赖的代码,您所依赖您没有办法在很快的时间协调到资源,那么您所负责的代码由于不具备单元测试环境没有办法进行测试,很可能存在极大的风险,因此如何测试您的代码,让他的质量达到百分之百的可用,这就是 Mock 存在的必要。
    当前市场上比较成熟的Mock框架主要有Mockito、EasyMock、Jmockit、PowerMock、Spock等等,SpringBoot默认的Mock框架是Mockito,和junit一样,只需要依赖spring-boot-starter-test就可以了。
  2. PowerMock入门
    PowerMock 现在目前提供了两套 UT(Unit Test)框架的封装,请看下图

image.png

本文中采用的是 Junit+PowerMock+Mockito 这样的组合来进行讲述的。
2.1 依赖
Maven依赖如下:

        <groupId>org.powermock</groupId>
        <artifactId>powermock-module-junit4</artifactId>
        <version>1.6.1</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-api-mockito</artifactId>
        <version>1.6.1</version>
        <scope>test</scope>
    </dependency>

2.2 Mock局部变量
首先新建一个员工类
package com.example.powermock.demo;

import lombok.Data;

/**

  • @description 员工类
  • @version V1.0
  • @date 2022/8/15 15:55

**/
@Data
public class Employee {

private Long id;
private String name;
private String hobbies;
private String mark;

}
再新增一个模拟数据库操作的Dao(接口方法用抛异常的方式模拟该接口不可用)
package com.example.powermock.demo.local.dao;

import com.example.powermock.demo.Employee;

/**

  • @description 员工Dao
  • @version V1.0
  • @date 2022/8/15 15:55

**/
public class EmployeeDao {

public int getTotal()
{
    throw new UnsupportedOperationException();
}

public void addEmployee(Employee employee)
{
    throw new UnsupportedOperationException();
}

}

2.2.1 有返回值
有如下Service的情况,在编码中经常遇到,但是其他Mock却对此束手无策:
package com.example.powermock.demo.local.service;

import com.example.powermock.demo.Employee;
import com.example.powermock.demo.local.dao.EmployeeDao;

/**

  • @description 员工service
  • @version V1.0
  • @date 2022/8/15 15:55

**/
public class EmployeeService {

public EmployeeService() {}
/**
 * 获取所有员工的数量.
 * @return
 */
public int getTotalEmployee()
{
    EmployeeDao employeeDao = new EmployeeDao();
    return employeeDao.getTotal();
}

}
我们采用在方法内部new出一个 EmployeeDao,而要测试这样的代码 EasyMock,Mockito 等显然力不从心,而PowerMock却毫无压力,请看下面的测试代码
package com.example.powermock.demo;

import com.example.powermock.demo.local.dao.EmployeeDao;
import com.example.powermock.demo.local.service.EmployeeService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

@RunWith(PowerMockRunner.class)
@PrepareForTest(EmployeeService.class)
public class EmployeeServiceLocalTest {

// 传统方法测试不通过

@Test

public void testGetTotalEmployee() {
    final EmployeeService service = new EmployeeService();
    int total = service.getTotalEmployee();
    assertEquals(10, total);
}

// PowerMock测试可以通过

@Test
public void testGetTotalEmployeeWithMock() {
    EmployeeDao employeeDao = PowerMockito.mock(EmployeeDao.class);
    try {
        PowerMockito.whenNew(EmployeeDao.class).withNoArguments().thenReturn(employeeDao);
        PowerMockito.when(employeeDao.getTotal()).thenReturn(10);
        EmployeeService service = new EmployeeService();
        int total = service.getTotalEmployee();
        assertEquals(10, total);
    } catch (Exception e) {
        fail("测试失败");
    }

}

}

2.2.2 void局部变量
在Servcie中增加一个void方法
public void createEmployee(Employee employee)

{
    EmployeeDao employeeDao = new EmployeeDao();
    employeeDao.addEmployee(employee);
}

Mock测试代码如下
@Test

public void testCreateEmployeeWithMock() {
    EmployeeDao employeeDao = PowerMockito.mock(EmployeeDao.class);
    try {
        PowerMockito.whenNew(EmployeeDao.class).withNoArguments()
                .thenReturn(employeeDao);
        Employee employee = new Employee();
        EmployeeService service = new EmployeeService();
        service.createEmployee(employee);
        Mockito.verify(employeeDao).addEmployee(employee);
    } catch (Exception e) {
        fail();
    }
}

2.3 Mock Static
对于公共方法,我们习惯于将其封装到工具类中,而工具类中的方法一般为static方法。
定义一个Service,定义两个方法,具体如下
package com.example.powermock.demo.staticmock.service;

import com.example.powermock.demo.Employee;
import com.example.powermock.demo.helloworld.dao.EmployeeDao;
import com.example.powermock.demo.staticmock.EmployeeUtils;

/**

  • @description 员工service
  • @version V1.0
  • @date 2022/8/15 15:55

**/
public class EmployeeService {

/**
 * 获取所有员工的数量.
 * @return
 */
public int getTotalEmployee()
{
    return EmployeeUtils.getEmployeeCount();
}

/**
 * 新增员工
 * @param employee
 */
public void createEmployee(Employee employee)
{
    EmployeeUtils.persistenceEmployee(employee);
}

}

我们的工具类Employee代码如下:
package com.example.powermock.demo.staticmock;

import com.example.powermock.demo.Employee;

public class EmployeeUtils {

// 获取员工数量
public static int getEmployeeCount()
{
    throw new UnsupportedOperationException();
}

//新增员工
public static void persistenceEmployee(Employee employee)
{
    throw new UnsupportedOperationException();
}

}

用常规的方式无法对static方法进行测试,而PowerMock对此游刃有余,代码如下
package com.example.powermock.demo;

import com.example.powermock.demo.staticmock.EmployeeUtils;
import com.example.powermock.demo.staticmock.service.EmployeeService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import static org.junit.Assert.assertEquals;

@RunWith(PowerMockRunner.class)
@PrepareForTest(EmployeeUtils.class)
public class EmployeeServiceStaticTest {

@Test
public void testGetTotalEmployeeWithMock() {
    PowerMockito.mockStatic(EmployeeUtils.class);
    PowerMockito.when(EmployeeUtils.getEmployeeCount()).thenReturn(10);
    final EmployeeService employeeService = new EmployeeService();
    int count = employeeService.getTotalEmployee();
    assertEquals(10, count);
}

@Test
public void testCreateEmployeeWithMock() {
    PowerMockito.mockStatic(EmployeeUtils.class);
    Employee employee = new Employee();
    PowerMockito.doNothing().when(EmployeeUtils.class);
    final EmployeeService employeeService = new EmployeeService();
    employeeService.createEmployee(employee);
    PowerMockito.verifyStatic();
}

}

2.4 Mock final

 一般的测试框架对final修饰的类或方法同样是束手无策,待测试接口如下:

Service
package com.example.powermock.demo.mockfinal.service;

import com.example.powermock.demo.Employee;
import com.example.powermock.demo.mockfinal.dao.EmployeeDao;

/**

  • @description 员工service
  • @version V1.0
  • @date 2022/8/15 15:55

**/
public class EmployeeService {

private EmployeeDao employeeDao;

public EmployeeService(EmployeeDao employeeDao)
{
    this.employeeDao = employeeDao;
}

public void createEmployee(Employee employee)
{
    employeeDao.addEmployee(employee);
}

}

带有final修饰接口的DAO
package com.example.powermock.demo.mockfinal.dao;

import com.example.powermock.demo.Employee;

/**

  • @description 员工Dao
  • @version V1.0
  • @date 2022/8/15 15:55

**/
public class EmployeeDao {

public final boolean addEmployee(Employee employee)
{
    throw new UnsupportedOperationException();
}

}

测试代码如下:
package com.example.powermock.demo;

import com.example.powermock.demo.mockfinal.dao.EmployeeDao;
import com.example.powermock.demo.mockfinal.service.EmployeeService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(EmployeeDao.class)
public class EmployeeServiceFinalTest {

@Test
public void testCreateEmployeeWithMock() {
    EmployeeDao employeeDao = PowerMockito.mock(EmployeeDao.class);
    Employee employee = new Employee();
    Mockito.when(employeeDao.addEmployee(employee)).thenReturn(true);
    EmployeeService employeeService = new EmployeeService(employeeDao);
    employeeService.createEmployee(employee);
    Mockito.verify(employeeDao).addEmployee(employee);
}

}

3 总结

PowerMock作为其他Mock框架的补充和扩展,有其自身独到的优势:final 类型的 class 和 method 的 mock 操作;完成对类方法(static)的 mock;完成对局部变量的 mock 等等。总之一句话:PowerMock 的出现为解决此类疑难问题另辟蹊径。
相关文章
|
1月前
|
Java 测试技术 Maven
Java一分钟之-PowerMock:静态方法与私有方法测试
通过本文的详细介绍,您可以使用PowerMock轻松地测试Java代码中的静态方法和私有方法。PowerMock通过扩展Mockito,提供了强大的功能,帮助开发者在复杂的测试场景中保持高效和准确的单元测试。希望本文对您的Java单元测试有所帮助。
161 2
|
5月前
|
Java 测试技术 Spring
详解单元测试问题之PowerMock不建议使用如何解决
详解单元测试问题之PowerMock不建议使用如何解决
55 1
|
6月前
|
XML 设计模式 Java
PowerMock:静态方法与私有方法测试
PowerMock是Java单元测试中扩展Mockito的框架,允许模拟静态方法、构造函数、私有方法和final类,以增强测试隔离和覆盖率。主要应用场景包括静态方法模拟、私有方法测试和构造函数/Final类模拟。然而,使用时需注意配置复杂性、避免过度使用、精确控制模拟行为和遵循最佳实践。示例展示了如何模拟静态方法,通过添加PowerMock依赖和使用PowerMockito.mockStatic进行静态方法的模拟和验证。正确使用PowerMock能提升测试质量,但应谨慎以保持代码可读性和测试有效性。
389 5
PowerMock:静态方法与私有方法测试
|
6月前
|
设计模式 Java 测试技术
Java一分钟之-PowerMock:静态方法与私有方法测试
【6月更文挑战第4天】PowerMock是扩展Mockito的框架,用于模拟静态方法、构造函数和私有方法,以增强Java单元测试的隔离性和覆盖率。核心应用场景包括静态方法模拟、私有方法测试和Final类模拟。常见问题包括配置复杂、过度使用和忽略模拟的真实行为。解决方案包括遵循官方文档、谨慎使用PowerMock、精确控制模拟逻辑和遵循最佳实践。示例展示了如何模拟静态方法,通过添加PowerMock依赖和使用`PowerMockRunner`、`PrepareForTest`注解,以及`PowerMockito.mockStatic`和`verifyStatic`方法进行测试。
385 0
Java一分钟之-PowerMock:静态方法与私有方法测试
|
6月前
|
XML 设计模式 Java
PowerMock的静态方法与私有方法怎么测试?
**PowerMock**是Java单元测试中的增强工具,扩展了Mockito,支持模拟静态方法、构造函数、私有方法和final类,促进更高测试覆盖率。它用于隔离依赖,测试静态方法和私有方法。常见问题包括配置复杂性、过度使用、忽略真实行为模拟和最佳实践。解决方案包括遵循官方文档、谨慎使用、精确模拟和测试后清理。示例展示了如何模拟静态方法,通过添加PowerMock依赖和使用`@RunWith(PowerMockRunner.class)`、`@PrepareForTest`注解,以及`PowerMockito.mockStatic()`进行静态方法模拟。
152 0
|
Java 测试技术 Maven
《Java单元测试实战》——基础知识:Java单元测试技巧之PowerMock(1)
《Java单元测试实战》——基础知识:Java单元测试技巧之PowerMock(1)
320 0
|
Java 测试技术
《Java单元测试实战》——基础知识:Java单元测试技巧之PowerMock(2)
《Java单元测试实战》——基础知识:Java单元测试技巧之PowerMock(2)
262 0
|
Java 测试技术
《Java单元测试实战》——基础知识:Java单元测试技巧之PowerMock(3)
《Java单元测试实战》——基础知识:Java单元测试技巧之PowerMock(3)
221 0
|
Java 测试技术
《Java单元测试实战》——基础知识:Java单元测试技巧之PowerMock(4)
《Java单元测试实战》——基础知识:Java单元测试技巧之PowerMock(4)
917 0
|
Java 测试技术
《Java单元测试实战》——基础知识:Java单元测试技巧之PowerMock(5)
《Java单元测试实战》——基础知识:Java单元测试技巧之PowerMock(5)
180 0