Mockito结合TestNG快速入门
什么是Mockito
Mockito 是一个专门用于 Java 的强大测试框架,主要用来创建和管理模拟对象,辅助开发者进行单元测试,具有以下特点和功能:
- 创建模拟对象:能通过简洁的语法创建类或接口的模拟版本,这些模拟对象可作为真实对象的替代品,在调试期间使用,帮助隔离外部依赖。比如在测试一个依赖其他服务(如账户服务、数据库访问服务等)的业务逻辑时,可模拟这些外部服务,避免在测试中涉及真实的复杂操作(如真的修改数据库数据、调用远程接口等) 。
- 定义行为:可以对模拟对象的方法进行 “桩(Stubbing)” 设置,即指定其返回值或让其抛出异常。例如,使用
when(...).thenReturn(...)
来定义方法返回特定值;用thenThrow()
模拟方法抛出异常,方便测试错误处理逻辑。 - 验证交互:提供验证机制,通过
verify(...)
方法检查模拟对象的方法是否按照预期被调用,以及调用的次数,从而验证代码与模拟对象之间的交互是否正确
引入的依赖
mockito-core
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>5.15.2</version><scope>test</scope>
</dependency>
testng
<!-- https://mvnrepository.com/artifact/org.testng/testng --><dependency><groupId>org.testng</groupId><artifactId>testng</artifactId><version>7.11.0</version><scope>test</scope></dependency>
为什么需要mock
可以知道这个方法的调用情况,调用了多少次参数就是多少
给这个对象的行为做一个定义,来指定返回结果或者指定特定的动作
当使用mock对象时,如果不对其行为进行定义,则mock对象方法的返回值为返回类型的默认值
验证和断言
验证:vertify()方法
验证是校验待验证的对象是否发生过某些行为
package com.example.testng;import org.mockito.Mockito;
import org.testng.annotations.Test;import java.util.Random;public class MockitoDemoTest {@Testpublic void test(){//mock一个对象Random mock= Mockito.mock(Random.class);//输出随机数System.out.println(mock.nextInt());//验证我们的mock对象是否用了nextInt()这个方法Mockito.verify(mock).nextInt();}
}
为什么我们的执行结果一直是0?
这是因为我们没有给我们的mock出来的对象定义行为
断言:Assert
判断我们返回的结果是否符合我们的预期
package com.example.testng;import org.mockito.Mockito;
import org.testng.Assert;
import org.testng.annotations.Test;import java.util.Random;public class MockitoDemoTest {@Testpublic void test() {//mock一个对象Random mock = Mockito.mock(Random.class);Mockito.when(mock.nextInt()).thenReturn(100);Assert.assertEquals(100, mock.nextInt());}
}
符合预期,我们成功通过
不符合预期,我们测试失败
package com.example.testng;import org.mockito.Mockito;
import org.testng.Assert;
import org.testng.annotations.Test;import java.util.Random;public class MockitoDemoTest {@Testpublic void test() {//mock一个对象Random mock = Mockito.mock(Random.class);Mockito.when(mock.nextInt()).thenReturn(100);Assert.assertEquals(101, mock.nextInt());}
}
给Mock对象进行打桩
打桩的意思其实就是对mock对象的行为进行定义
打桩的话是mockito里面的when()方法,定义我们的行为
@Mock注解
我们的mock注解必须搭配MockitoAnnotations.openMocks(testClass)方法一起使用
我们使用@Mock注解来Mock对象,而不是在代码里面手动Mock我们的对象
package com.example.testng;import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.testng.Assert;
import org.testng.annotations.Test;import java.util.Random;public class MockitoDemoTest {@Mockprivate Random mock;@Testpublic void test() {MockitoAnnotations.openMocks(this);Mockito.when(mock.nextInt()).thenReturn(100);Assert.assertEquals(100, mock.nextInt());}
}
@BeforeTets与@AfterTest
package com.example.testng;import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.testng.Assert;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;import java.util.Random;public class MockitoDemoTest {@Mockprivate Random mock;@BeforeTestvoid setup() {System.out.println("测试前的准备");}@AfterTestvoid end() {System.out.println("测试结束");}@Testpublic void test() {MockitoAnnotations.openMocks(this);Mockito.when(mock.nextInt()).thenReturn(100);Assert.assertEquals(100, mock.nextInt());}
}
Spy方法与@Spy注解
spy方法与mock方法的不同是
1.被spy的对象会走真实的方法而mock对象不会
2.spy{}方法的参数是对象实例,mock的参数是class
package com.example.testng;import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.testng.Assert;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;import java.util.Random;public class MockitoDemoTest {@Spyprivate Random mock;@BeforeTestvoid setup() {MockitoAnnotations.openMocks(this);}@Testpublic void test() {Assert.assertEquals(100, mock.nextInt());}
}
我们没有打桩,而且我们是Spy注解,所以我们不会输出0,而是会去调用真正的Random方法
如果我们打桩了的话,那还是会按照我们打桩定义的规则来
打桩与Mock静态方法
4个常见的打桩方法
thenCallRealMethod
thenReturn
thenThrow
thenAnswer
package com.example.testng;import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.testng.Assert;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;import java.util.Random;import static org.mockito.Mockito.when;public class MockitoDemoTest {@Spyprivate Random mock;@BeforeTestvoid setup() {MockitoAnnotations.openMocks(this);}@Testpublic void test() {//调用真实方法when(mock.nextInt()).thenCallRealMethod();//定义返回值when(mock.nextInt()).thenReturn(1);//定义返回什么错误when(mock.nextInt()).thenThrow(new RuntimeException());//自定义响应逻辑when(mock.nextInt()).thenAnswer(new Answer<Integer>() {@Overridepublic Integer answer(InvocationOnMock invocation) throws Throwable {// 这里可以编写自定义的逻辑// invocation 包含了方法调用的信息,例如方法名、参数等// 这里简单返回一个固定值 42return 42;}});}
}
Mock静态方法
依赖
mockito-inline依赖包含了mockito-code依赖
注意我们使用mockito-inline依赖的时候要把mockito-code依赖注释掉,我们不能同时引用
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-inline -->
<dependency><groupId>org.mockito</groupId><artifactId>mockito-inline</artifactId><version>5.2.0</version><scope>test</scope>
</dependency>
在软件开发的单元测试中,有时候我们需要对静态方法进行模拟(Mock),这样可以更好地控制测试环境、隔离依赖,提高测试的可维护性和稳定性。下面详细介绍模拟静态方法的相关内容,以 Java 语言结合 Mockito 框架为例
为什么需要 Mock 静态方法
隔离依赖:静态方法可能依赖于外部资源,如文件系统、数据库等。在单元测试中,我们希望避免这些外部依赖的影响,通过模拟静态方法可以将测试与外部资源隔离开来
控制返回值:静态方法的返回值可能受到多种因素的影响,通过模拟静态方法,我们可以精确控制其返回值,从而更方便地验证业务逻辑
使用例子
我们这个类里面有个静态方法
public class StaticUtils {public static int add(int a, int b) {return a + b;}
}
使用mockStatic
使用 try-with-resources语句确保 MockedStatic
对象在使用完毕后自动关闭,避免对后续测试产生影响
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mockStatic;public class StaticUtilsTest {@Testpublic void testMockStaticMethod() {// 使用 try-with-resources 语句创建 MockedStatic 对象try (MockedStatic<StaticUtils> mockedStatic = mockStatic(StaticUtils.class)) {// 定义静态方法的行为mockedStatic.when(() -> StaticUtils.add(2, 3)).thenReturn(10);// 调用静态方法int result = StaticUtils.add(2, 3);// 验证结果assertEquals(10, result);}}
}
单元测试如何提高我们的代码覆盖率
@InjectMocks注解
剩下的@mock和@spy注解修饰的对象会自动注入到被InjectMocks注解修饰的对象里面