一、Mockito mock静态方法
mockito可以mock掉Java中的静态方法,但需要依赖一个第三方库Power Mock。Power Mock是一个Java测试框架,它增强了Mockito和EasyMock两个框架的功能。
首先,需要在pom.xml中引入以下依赖:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4-rule-agent</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>
接下来我们会创建一个名为 Calculator 的类,其中有一个名为 add 的静态方法。然后,我们将会测试一个使用 add 方法的类:CalculatorService。
public class Calculator {
public static int add(int x, int y) {
return x + y;
}
}
public class CalculatorService {
public int sum(int x, int y) {
return Calculator.add(x, y);
}
}
现在我们假设要测试 CalculatorService 的 sum 方法。由于用到了 Calculator.add 方法,我们需要使用 Mockito 和 PowerMockito 来 mock 这个方法。
@RunWith(PowerMockRunner.class)
@PrepareForTest(Calculator.class)
public class CalculatorServiceTest {
@Test
public void testSum() {
PowerMockito.mockStatic(Calculator.class);
Mockito.when(Calculator.add(1, 2)).thenReturn(3);
CalculatorService calculatorService = new CalculatorService();
int sum = calculatorService.sum(1, 2);
Assert.assertEquals(3, sum);
}
}
在这个测试用例中,我们首先使用 PowerMockito.mockStatic(MockedClass.class) 来 mock 掉 Calculator 类中的静态方法 add。接着,使用 Mockito.when(MockedClass.method(params)).thenReturn(returnValue) 来指定 add 方法在参数为 1 和 2 时的返回值为 3。最后调用 CalculatorService 的 sum 方法进行测试。
二、Mockito指定对象
mockito可以mock掉Java中的具体对象。我们可以使用 Mockito.mock(Class
举个例子,我们来模拟一个 Invoice 对象,其中含有两个方法:addItem 和 removeItem。addItem 方法将 item 添加到该发票,并且更新 item 的总价。removeItem 方法将指定的 item 从发票中删除,并且更新 item 的总价。
public class Invoice {
private List<Item> items = new ArrayList<>();
private BigDecimal total = BigDecimal.ZERO;
public void addItem(Item item) {
items.add(item);
total = total.add(item.getPrice());
}
public void removeItem(Item item) {
items.remove(item);
total = total.subtract(item.getPrice());
}
public BigDecimal getTotal() {
return total;
}
}
使用 Mockito.mock(Class<T> classToMock) 方法来 mock Invoice 类:
@Test
public void testMock() {
Invoice invoice = Mockito.mock(Invoice.class);
Mockito.when(invoice.getTotal()).thenReturn(BigDecimal.valueOf(100));
Assert.assertEquals(BigDecimal.valueOf(100), invoice.getTotal());
}
在这个测试用例中,我们使用 mock 方法来 mock 一个 Invoice 对象。当调用 getTotal() 方法时,该对象将返回一个固定值 100。
现在我们来使用 Mockito.spy(T object) 方法来 spy 一个 Invoice 对象:
@Test
public void testSpy() {
Item item = new Item("item1", BigDecimal.valueOf(50));
Invoice invoice = new Invoice();
Invoice spyInvoice = Mockito.spy(invoice);
spyInvoice.addItem(item);
spyInvoice.addItem(item);
spyInvoice.removeItem(item);
Assert.assertEquals(BigDecimal.valueOf(50), spyInvoice.getTotal());
}
在这个测试用例中,我们首先实例化了一个正常的 Invoice 对象和一个 spied Invoice 对象。然后我们通过 spyInvoice.addItem(item) 和 spyInvoice.removeItem(item) 的操作来间接修改 spied Invoice 对象的 total 属性。最后,我们使用 assertEquals 来确保 spied Invoice 对象的总价确实为 50。上述代码的正确性可以用下图来描述:
三、Mockito mock void方法
针对 void 方法我们可以使用以下两种方式进行 Mock 单元测试:
第一种方法是使用 Mockito.doNothing().when(mockedObject).voidMethod() 来 mock void 方法:
public class Service {
public void doSomething(int i) {
if (i == 0) {
throw new IllegalArgumentException("i can not be zero");
}
}
}
@Test
public void testVoidMethod() {
Service service = Mockito.mock(Service.class);
Mockito.doNothing().when(service).doSomething(0);
}
在这个测试用例中,我们使用 Mockito.mock(Class<T> classToMock) 方法来 mock 一个 Service 对象。然后使用 Mockito.doNothing().when(service).doSomething(0) 来指定当 service.doSomething(0) 方法被调用时,什么事情都不用做。
另一种方法是使用 Mockito.verify(mockedObject).voidMethod() 来验证 mock 的 void 方法是否被调用过:
public class Service {
public void doSomething(int i) {
if (i == 0) {
throw new IllegalArgumentException("i can not be zero");
}
}
}
@Test
public void testVerifyVoidMethod() {
Service service = Mockito.mock(Service.class);
service.doSomething(1);
Mockito.verify(service).doSomething(1);
}
在这个测试用例中,我们使用 Mockito.mock(Class<T> classToMock) 方法来 mock 一个 Service 对象。然后执行 service.doSomething(1) 方法,并使用 Mockito.verify(service).doSomething(1) 验证该方法确实被调用过一次。
四、Mockito教程
Mockito 是一个流行的 Java 单元测试框架,用于为 Java 开发人员提供方便的 mock 对象功能。Mockito 的主要优点包括易于学习、基本上无需编写代码、在 JUnit 和 TestNG 中无缝集成、可读性和良好的 Javadoc 文档。Mockito 使测试变得容易简单,并提供了一个强大的 API 来创建灵活的测试。
Mockito 的 API 主要围绕 mock 和 spy 两个方法。mock 方法将创建一个新的 mock 对象,并模拟指定类的行为。spy 方法将创建真实对象的模拟对象,并允许部分方法进行模拟、部分方法进行真实调用。
在 Mockito 中,重要的 API 包括 Mockito.mock()、Mockito.when()、Mockito.verify()、Mockito.doReturn() 等等。Mockito API 的使用方式非常流畅,这使得 Mockito 可以嵌入到开发过程中,从而提高代码质量和可维护性。以下是一个基本的 Mockito 教程,介绍如何开始使用 Mockito 来进行 Java 单元测试。
1. 在 Maven 项目的 pom.xml 文件中添加以下依赖项:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.8.0</version>
<scope>test</scope>
</dependency>
2. 创建一个 Java 类(下面例子中是 Calculator),并实现一些方法:
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public int subtract(int a, int b) {
return a - b;
}
public int multiply(int a, int b) {
return a * b;
}
public int divide(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("division by zero");
}
return a / b;
}
}
3. 创建测试类(下面例子中是 CalculatorTest):
public class CalculatorTest {
@Test
public void testAdd() {
Calculator calculator = Mockito.mock(Calculator.class);
Mockito.when(calculator.add(1, 2)).thenReturn(3);
Assert.assertEquals(calculator.add(1, 2), 3);
}
@Test
public void testDivide() {
Calculator calculator = Mockito.spy(Calculator.class);
Mockito.when(calculator.divide(6, 2)).thenReturn(3);
try {
Assert.assertEquals(calculator.divide(6, 2), 3);
} catch (Exception e) {
fail("will not have this exception");
}
}
}
4. 运行这些测试。
通过一个简单的四步操作,我们就可以使用 Mockito 进行单元测试。
五、Mockito中文文档
Mockito 中文文档详细的介绍了 Mockito 的用法,回答了许多人在使用 Mockito 过程中可能遇到的问题。文档主要包括:
1. 快速入门:介绍基本的 mock 对象、stub 方法调用等用法
2. 验证 Mock 的调用:介绍如何验证方法的调用次数,以及如何验证连续调用
3. 参数匹配器:介绍如何使用参数匹配器以及一些用法例子
4. Stubbing 流:介绍如何从一个被 Mock 的对象中创建流,并对流进行操作、返回流
5. Mockito 外部依赖:介绍如何使用 Mockito 处理外部依赖
除了这些,该文档还包括 Mockito.Javadoc,Mockito 示例和 Mockito-Framework-3.x.pdf 等内容。
Mockito 中文文档为中文用户提供了方便实用的方法,在使用 Mockito 进行单元测试时有很大的帮助。
六、Mockito怎么throw一个私有方法
Mockito 的 spy 方法不仅可以 spy 一个对象的部分方法。如果 spy 对象内部依然有私有方法,我们还可以使用 Mockito.spy(object) 这个功能,对私有方法进行 mock,即下面的例子
public class PrivateServiceImpl implements PrivateService {
private String doPrivate(String str) {
return str + " world!";
}
}
public interface PrivateService {
String doPrivate(String str);
}
@Test
public void testThrowPrivateMethodException() throws Exception {
PrivateServiceImpl spy = Mockito.spy(new PrivateServiceImpl());
Mockito.when(spy, Methods.PRIVATE.method(PrivateServiceImpl.class, "doPrivate", String.class))
.thenThrow(new IllegalStateException("test"));
Class<?>[] args = new Class<?>[] {String.class};
Method method = PrivateServiceImpl.class.getDeclaredMethod("doPrivate", args);
method.setAccessible(true);
try {
method.invoke(spy, "hello,");
} catch (InvocationTargetException e) {
Assert.assertEquals(IllegalStateException.class, e.getTargetException().getClass());
}
}
在上述