赞
踩
Mock 可以理解为创建一个虚假的对象,或者说模拟出一个对象,在测试环境中用来替换掉真实的对象,以达到我们可以。验证该对象的某些方法的调用情况,调用了多少次,参数是多少。给这个对象的行为做一个定义,来指定返回结果或者指定特定的动作
- <dependency>
- <groupId>org.junit.jupiter</groupId>
- <artifactId>junit-jupiter</artifactId>
- <scope>5.8.2</scope>
- </dependency>
-
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <scope>4.3.1</scope>
- </dependency>
mock 方法来自 org.mockito.Mock,它表示可以mock 一个对象或者是接口。
public static <T> T mock(Class<T> classToMock)
对于以下简单方法的mock实例:
- public class MockitoDemo {
- public int add (int a, int b){
- return a + b;
- }
- }
在类名上,鼠标右键,Generate--> Test 勾上要测试的方法,就会创建一个测试类
- @Test
- void add() {
- //mock方法来自org.mockito.Mock,它表示可以mock一个对象或者接口
- //官方文档中public static <T> T mock(Class<T> classToMock),这个classToMock 是待 mock 对象的 class 类
- //1.mock 一个对象的class类
- Random random = Mockito.mock(Random.class,"test");
- //2.使用random的nextInt方法
- System.out.println(random.nextInt());
- //3.验证是校验待验证的对象是否发生过某些行为,Mockito 中验证的方法是: verify
- Mockito.verify(random).nextInt();
- //4.Verify 配合 times() 方法,可以校验某些操作发生的次数
- Mockito.verify(random, Mockito.times(1)).nextInt();
- }
打桩的意思就是给 mock 对象规定一行的行为,使其按照我们的要求来执行具体的操作
Random random = Mockito.mock(Random.class,"test");
//5.打桩 用when方法
Mockito.when(random.nextInt()).thenReturn(100);
打桩之后会用断言来验证;打桩的是期望值
断言使用到的类是Assertions.里面的两个参数,第一个是期望值,第二个是实际值;
Random random = Mockito.mock(Random.class,“test”);
Assertions.assertEquals(100, random.nextInt());
当使用mock 对象时,如果不对其行为进行定义,则mock 对象方法的返回值为返回类型的默认值。
想当于是快速的创建mock,模拟方法。不再用
Random random = Mockito.mock(Random.class,“test”);
而是直接在类中,方法上面:
@Mock
private Random random;
@Mock 注解的官方注意事项
Shorthand for mocks creation - @Mock annotation
Important! This needs to be somewhere in the base class or a test runner:
快速 mock 的方法,使用 @mock 注解。
mock 注解需要搭配MockitoAnnotarions.openMocks(testClass)方法一起使用
- @Mock
- private Random random;
-
- @Test
- void add() {
- //使@Mock注解生效
- MockitoAnnotations.openMocks(this);
- //打桩
- Mockito.when(random.nextInt()).thenReturn(100);
- //断言
- Assertions.assertEquals(100,random.nextInt());
- }
- @Mock
- private Random random;
-
- @BeforeEach
- void setUp(){
- System.out.println("测试前的准备");
- //使@Mock注解生效
- MockitoAnnotations.openMocks(this);
- }
-
- @Test
- void add() {
- //打桩
- Mockito.when(random.nextInt()).thenReturn(100);
- //断言
- Assertions.assertEquals(100,random.nextInt());
- }
-
- @AfterEach
- void after(){
- System.out.println("测试结束");
- }
spy()方法与mock()方法不同的是
1.被 spy 的对象会走真实的方法,而 mock 对象是模拟的方法 2.spy() 方法的参数是对象实例,mock 的参数是 class
- @Spy
- private MockitoDemo mockitoDemo;
-
- @BeforeEach
- void setUp(){
- System.out.println("测试前的准备");
- MockitoAnnotations.openMocks(this);
- }
-
- @Test
- void add() {
- /*
- //这是直接运行,和验证的写法
- int addResult = mockitoDemo.add(1, 2);
- Assertions.assertEquals(3,addResult);
- */
-
-
- //更应该进行打桩,设定一个返回值
- //当使用mock 对象时,如果不对其行为进行定义,则mock 对象方法的返回值为返回类型的默认值
- Mockito.when(mockitoDemo.add(1,2)).thenReturn(3);
- //然后进行断言,没有打桩走真实方法,打桩走模拟的方法
- Assertions.assertEquals(3,mockitoDemo.add(1,2));;
- }
- /*
- //.thenThtow() 抛出异常,因为在又抛出异常的地方,要考虑到正常通过和抛出异常两面
- Mockito.when(mockitoDemo.add(1,2)).thenThrow(new RuntimeException("mockitoDemo.add的运行时异常"));
- Assertions.assertEquals(3,mockitoDemo.add(1,2));
- */
-
- /*
- //.thenCallRealMethod() 走真实的方法。
- // 这里的场景是@Mock的情况中,可以先走一次mock方法,再走一次真实的方法
- Mockito.when(mockitoDemo.add(1,2)).thenCallRealMethod();
- Assertions.assertEquals(3,mockitoDemo.add(1,2));
- */
--classToMock待 mock 对象的 class 类 --返回mock 出来的类
实例:使用mock 方法mock 一个类
Random random = Mockito.mock(Random.class,“test");
验证是校验待验证的对象是否发生过某些行为,Mockito 中验证的方法是: verify。
- verify(mock).someMethod("some arg");
- verify(mock,times(1)).someMethod("some arg");
使用 verify 验证: Verify 配合 time() 方法,可以校验某些操作发生的次数
静态方法需要更换mockito依赖
- <!--mockito-inline 比 mockito-core 支持的mock方法更多,比如mock静态方法等。
- 且包含了mockito-core且这两个依赖不能同时使用-->
-
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-inline</artifactId>
- <scope>4.3.1</scope>
- </dependency>
代码实现
- /**
- * desc:mock静态方法
- * 包括无参和有参的静态方法
- * @author zsc
- * @createTime 2023/10/16 8:53
- */
- class StaticDemoTest {
- /**
- * range
- * 有参静态方法 的mock测试
- * @param
- * @return void
- * @author zsc
- * @date 2023/10/16
- **/
- @Test
- void range() {
- //mock方法的导入
- MockedStatic<StaticDemo> staticDemoMockedStatic = Mockito.mockStatic(StaticDemo.class);
- //打桩
- staticDemoMockedStatic.when(()->StaticDemo.range(2,6)).thenReturn(Arrays.asList(10,11,12));
- //断言
- Assertions.assertTrue(StaticDemo.range(2,6).contains(10));
-
- //关闭mock
- staticDemoMockedStatic.close();
- }
-
- /**
- * name
- * 有参静态方法的mock测试
- * @param
- * @return void
- * @author zsc
- * @date 2023/10/16
- **/
- @Test
- void name() {
- //mock方法的导入
- MockedStatic<StaticDemo> staticDemoMockedStatic = Mockito.mockStatic(StaticDemo.class);
- //打桩
- staticDemoMockedStatic.when(StaticDemo::name).thenReturn("bilibili");
- //断言
- Assertions.assertEquals("bilibili",StaticDemo.name());
- //关闭mock
- staticDemoMockedStatic.close();
- }
-
- }
关闭mock :staticDemoMockedStatic.close();一定要关闭,不然每个@Test都可以运行的情况下,整体的class StaticDemoTest{}是不能运行的
模拟(mock)一些依赖的对象来隔离被测试类的外部依赖比如
- @Mock
- private UserMapper userMapper
-
- @InjectMocks
- private UserServiceImpl userService;
@InjectMocks
注解会根据类型(或名称)匹配,在被测试类中自动查找并注入模拟对象
- try {
- userService.queryUserPage(queryUserPageBO);
- } catch (Exception e) {
- Assertions.assertTrue(e instanceof CustomException);
- }
-
- 这里用到了userService.queryUserPage()一定会调用mapper层,但是上面@InjectMocks会把mock出来的userMapper加入进来。不会去测试类外面去找。
以在 IntelliJ IDEA 中开启分屏功能。如果您的键盘布局与默认配置不同,也可以通过打开 "Settings/Preferences" -> "Keymap" 菜单,搜索 "split" 来查找并配置适合您的分屏快捷键
我的快捷键
Ctrl + Shift +CAPS LK
写类的 mock 取决于你的测试目标和测试策略。 mock 的目的是用虚拟对象替代真实对象,以模拟特定行为或返回值,从而进行单元测试。
在编写单元测试时,通常会针对不同层次的组件进行测试。如果你的目标是测试 Controller 层的代码逻辑,那么你可能需要编写 Controller 层的 mock 对象来模拟请求和验证 Controller 的行为。这样你可以在不涉及实际服务调用的情况下对 Controller 的逻辑进行测试。
然而,如果你的目标是测试 Service 层的代码逻辑,你可以创建 Service 层的 mock 对象来模拟对其他依赖组件(如 DAO 层、外部服务等)的调用,而不需要关心 Controller 层。这样可以更加专注地测试 Service 层的业务逻辑。
总而言之,编写哪些类的 mock 取决于你的测试目标和测试策略。根据需要,你可以选择编写 Controller 层的 mock、Service 层的 mock 或其他相关组件的 mock,以便针对不同的层次进行单元测试
打桩时需要传入参数的首先可以试试传入any();
打桩返回参数是list,比较复杂,尝试用arraylist
类似以下的用法:
Mockito.when(userMapper.queryUserList(any(),any())).thenReturn(null);Mockito.when(userMapper.queryUserPage(querryUserPagePO,pageParams)).thenReturn(arrayList);
请注意any()是来自于mockito-core依赖的
import static org.mockito.ArgumentMatchers.any;
而any()来自于
Maven: org.mockito:mockito-core:3.6.28 (mockito-core-3.6.28.jar)所以mock测试的时候采用mockito-core 依赖
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>4.3.1</scope>
</dependency><dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>5.8.2</scope>
</dependency>
然后需要遇到空指针错误,需要传入一个有值的参数;
如果是int,string等基础类型可以直接传值;这个时候any()一般也可以用;
像是ToracledemoUserBO 这种有字段的类型,
首先尝试new出来。毕竟直接new出来的类也不是null的;
其次可以向new出来的类中去set数据。
Mockito编写Service层单元测试https://blog.csdn.net/u012760435/article/details/90643872 mock详细教程入门这一篇就够了https://blog.csdn.net/m0_58026506/article/details/126897449
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。