德国世界杯_2012年世界杯 - fyycdq.com

德国世界杯_2012年世界杯 - fyycdq.com

Mockito 使用

Mockito 是 Java 的模拟测试框架,通过 Mockito 可以创建和配置 Mock 对象,简化外部依赖的类的测试。

Mocikto 教程

org.junit.jupiter

junit-jupiter-engine

5.10.1

test

org.mockito

mockito-core

5.7.0

test

初始化注解 + ExtendWith注释 + BeforeEach手动初始化注解 + ExtendWithpackage org.example;

import org.example.service.UserService;

import org.junit.jupiter.api.Assertions;

import org.junit.jupiter.api.Test;

import org.junit.jupiter.api.extension.ExtendWith;

import org.mockito.Mock;

import org.mockito.Mockito;

import org.mockito.Spy;

import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)

public class Init01Test {

@Mock // @InjectMocks —— 前者不调用实际方法,后者调用实际方法

private UserService mockUserService;

@Spy

private UserService spyUserService;

@Test

void test() {

Assertions.assertTrue(Mockito.mockingDetails(mockUserService).isMock());

Assertions.assertFalse(Mockito.mockingDetails(mockUserService).isSpy()); // 💡 mock is not spy —— mock 实际为空调用,而 spy 需要监听实际方法调用,因此冲突

Assertions.assertTrue(Mockito.mockingDetails(spyUserService).isSpy());

Assertions.assertTrue(Mockito.mockingDetails(spyUserService).isMock()); // 💡 spy is mock —— 在 spy 监听实际方法调用的同时,提供 mock 改变返回值的功能!

}

}

org.mockito

mockito-junit-jupiter

5.7.0

test

注释 + BeforeEachpackage org.example;

import org.example.service.UserService;

import org.junit.jupiter.api.Assertions;

import org.junit.jupiter.api.BeforeEach;

import org.junit.jupiter.api.Test;

import org.mockito.Mock;

import org.mockito.Mockito;

import org.mockito.MockitoAnnotations;

import org.mockito.Spy;

public class Init02Test {

@Mock

private UserService mockUserService;

@Spy

private UserService spyUserService;

@BeforeEach

void beforeEach() {

MockitoAnnotations.openMocks(this);

}

@Test

void test() {

Assertions.assertTrue(Mockito.mockingDetails(mockUserService).isMock());

Assertions.assertTrue(Mockito.mockingDetails(spyUserService).isSpy());

}

}

手动初始化package org.example;

import org.example.service.UserService;

import org.junit.jupiter.api.Assertions;

import org.junit.jupiter.api.BeforeEach;

import org.junit.jupiter.api.Test;

import org.mockito.Mockito;

public class Init03Test {

private UserService mockUserService;

private UserService spyUserService;

@BeforeEach

public void beforeEach() {

mockUserService = Mockito.mock(UserService.class);

spyUserService = Mockito.spy(UserService.class);

}

@Test

void test() {

Assertions.assertTrue(Mockito.mockingDetails(mockUserService).isMock());

Assertions.assertTrue(Mockito.mockingDetails(spyUserService).isSpy());

}

}

提示

Mock —— 从类型创建,不调用实际方法Spy —— 对现有实例的封装,调用被封装实例的实际方法基本使用Mock —— 根据类名创建一个 “空对象”,可以对其返回值进行定制,若无定制返回默认值Spy —— 根据实际对象创建一个 “代理对象”,Captor —— 捕获传入的参数值InjectMock —— 自动注入相关引用mock对于一个 mock 对象,我们可以指定返回值和执行特定的动作。 当然,也可以不指定。

如果不指定返回值的话,一个 mock 对象的所有非 void 方法都将返回默认值:

int、long 类型方法将返回 0boolean 方法将返回 false对象方法将返回 null 等等而 void 方法将什么都不做。

package org.example;

import org.example.entity.UserDTO;

import org.example.service.UserService;

import org.junit.jupiter.api.Assertions;

import org.junit.jupiter.api.Test;

import org.junit.jupiter.api.extension.ExtendWith;

import org.junit.jupiter.api.function.Executable;

import org.mockito.Mock;

import org.mockito.exceptions.misusing.UnnecessaryStubbingException;

import org.mockito.junit.jupiter.MockitoExtension;

import org.mockito.stubbing.Answer;

import java.util.ArrayList;

import java.util.List;

import static org.mockito.ArgumentMatchers.any;

import static org.mockito.Mockito.*;

/**

* 模拟返回结果/抛出异常

*/

@ExtendWith(MockitoExtension.class)

public class Mock01Test {

@Mock

private UserService mockUserService;

/**

* 测试 when 的 thenReturn 方法

* 💡调用多个 thenReturn 时,会按顺序返回

*/

@Test

void test_when_thenReturn() {

List returnUserAll01 = List.of(new UserDTO());

List returnUserAll02 = List.of(new UserDTO());

when(mockUserService.getUserAll()).thenReturn(returnUserAll01).thenReturn(returnUserAll02); // 顺序返回

Assertions.assertArrayEquals(returnUserAll01.toArray(), mockUserService.getUserAll().toArray());

Assertions.assertArrayEquals(returnUserAll02.toArray(), mockUserService.getUserAll().toArray());

Assertions.assertArrayEquals(returnUserAll02.toArray(), mockUserService.getUserAll().toArray()); // 返回最后一个 thenReturn 设定

}

/**

* 另一种写法

*/

@Test

void test_when_thenReturn_2() {

List returnUserAll01 = List.of(new UserDTO());

doReturn(returnUserAll01).when(mockUserService).getUserAll();

Assertions.assertArrayEquals(returnUserAll01.toArray(), mockUserService.getUserAll().toArray());

}

/**

* ⚠️定义了 when 就要调用,否则抛异常 {@link UnnecessaryStubbingException}

* ⚠️如果确定方法 mock 后不调用,一种处理方法是使用 lenient() 提供的方法进行 mock

* 💡另一种方法是在 mock 环境建立前设置宽松的 mock 行为: Mockito.mockitoSession().initMocks(this).strictness(Strictness.STRICT_STUBS).startMocking();

*/

@Test

void test_UnnecessaryStubbingException_lenient() {

// ↓ or @Mock(lenient = true)

lenient().when(mockUserService.getUserAll()).thenReturn(new ArrayList<>()); // this won't get called

}

/**

* 动态返回

*/

@Test

void test_thenAnswer() {

UserDTO param = new UserDTO();

when(mockUserService.getUserList(any(UserDTO.class))).thenAnswer((Answer>) invocation -> {

UserDTO argument = invocation.getArgument(0);

return List.of(argument); // 💡运行时获取传入参数(而不是 when 时就指定)

});

Assertions.assertArrayEquals(List.of(param).toArray(), mockUserService.getUserList(param).toArray());

}

/**

* 模拟异常抛出

*/

@Test

void test_doThrow() {

doThrow(ArithmeticException.class).when(mockUserService).getUserAll();

Assertions.assertThrows(RuntimeException.class, new Executable() {

@Override

public void execute() throws Throwable {

mockUserService.getUserAll();

}

});

}

}

spypackage org.example;

import org.example.service.UserService;

import org.junit.jupiter.api.Assertions;

import org.junit.jupiter.api.Test;

import org.junit.jupiter.api.extension.ExtendWith;

import org.junit.jupiter.api.function.Executable;

import org.mockito.Mockito;

import org.mockito.Spy;

import org.mockito.exceptions.verification.WantedButNotInvoked;

import org.mockito.junit.jupiter.MockitoExtension;

import java.util.ArrayList;

import java.util.List;

import static org.mockito.Mockito.mockingDetails;

import static org.mockito.Mockito.verify;

@ExtendWith(MockitoExtension.class)

public class Spy01Test {

@Spy

private UserService userService;

@Test

void test_spy_is_mock() {

Assertions.assertTrue(mockingDetails(userService).isMock()); // 既是 spy 也是 mock

Assertions.assertTrue(mockingDetails(userService).isSpy());

}

/**

* 测试 verify 抛出异常的情况

*/

@Test

void test_verify() {

Assertions.assertThrows(WantedButNotInvoked.class, new Executable() {

@Override

public void execute() throws Throwable {

verify(userService).getUserAll(); // 校验发现方法未被调用(至少一次),则抛出异常

// verify(userService, times(1)).getUserAll(); // 等价 ↑

}

});

userService.getUserAll();

verify(userService).getUserAll(); // 调用后再校验,则不报错

}

/**

* 测试 spy 的实例与 spy 是否关联

*/

@Test

void test_instance_change() {

List list = new ArrayList<>();

List spy = Mockito.spy(list);

spy.add("1");

Assertions.assertEquals(1, spy.size());

Assertions.assertEquals(0, list.size()); // spy 与实例并不关联,相互独立

}

}

captorpackage org.example;

import org.example.entity.UserDTO;

import org.example.service.UserService;

import org.junit.jupiter.api.Assertions;

import org.junit.jupiter.api.Test;

import org.junit.jupiter.api.extension.ExtendWith;

import org.mockito.*;

import org.mockito.junit.jupiter.MockitoExtension;

import static org.mockito.Mockito.*;

public @ExtendWith(MockitoExtension.class)

class Captor01Test {

@Mock

private UserService userService;

@Captor

ArgumentCaptor argCaptor;

@Test

void test_when_capture() {

when(userService.getUserList(argCaptor.capture())).thenReturn(null);

UserDTO userDTO = new UserDTO();

Assertions.assertNull(userService.getUserList(userDTO));

Assertions.assertEquals(userDTO, argCaptor.getValue());

}

@Test

void test_verify_capture() {

UserDTO userDTO = new UserDTO();

userService.getUserList(userDTO);

verify(userService, times(1)).getUserList(argCaptor.capture());

Assertions.assertEquals(userDTO, argCaptor.getValue());

}

}

区别 @Mock/@Spy/@Captor/@InjectMockpackage org.example;

import org.junit.jupiter.api.Assertions;

import org.junit.jupiter.api.Test;

import org.junit.jupiter.api.extension.ExtendWith;

import org.mockito.*;

import org.mockito.junit.jupiter.MockitoExtension;

import java.util.ArrayList;

import java.util.List;

import static org.mockito.Mockito.mockingDetails;

import static org.mockito.Mockito.verify;

/**

* 区分 @Mock、@Spy、@Captor、@InjectMock

*/

@ExtendWith(MockitoExtension.class)

public class DiffTypesTest {

@Mock

private List mock;

@Spy

private List spyNoInstance; // 💡如果没有实例化,则退化成 mock (即不调用实际方法)

@Spy

private List spy = new ArrayList<>();

@Captor

private ArgumentCaptor captor; // 用于获取入参

/**

* 自动注入相关引用

*/

@InjectMocks

private InjectMockClass injectMockClass = new InjectMockClass();

/**

* \@Mock 不实际调用方法

*/

@Test

void test_mock_do_nothing() {

mock.add("1");

Assertions.assertEquals(0, mock.size());

}

/**

* \@Spy 实际调用方法

*/

@Test

void test_spy_do_exactly() {

spy.add("1");

Assertions.assertEquals(1, spy.size());

spyNoInstance.add("1");

Assertions.assertEquals(0, spyNoInstance.size());

}

@Test

void test_captor() {

spy.add("1");

verify(spy).add(captor.capture());

Assertions.assertEquals(1, spy.size());

Assertions.assertEquals("1", captor.getValue());

}

/**

* \@InjectMock 注入相关 @Mock/@Spy 进入对象属性中

*/

@Test

void test_injectMock() {

// 自动注入相关属性

Assertions.assertEquals(mock, injectMockClass.mock);

Assertions.assertEquals(spy, injectMockClass.spy);

Assertions.assertFalse(mockingDetails(injectMockClass).isMock()); // 被注入属性的对象默认不是 Mock

Assertions.assertEquals(1, injectMockClass.doSomething()); // 实际执行方法

}

private static class InjectMockClass {

private List mock;

private List spy;

int doSomething() {

return 1;

}

}

}

verify通过 verify 来判断方法内部实现是否符合预想。

通过对 verify(T mock)/verify(T mock, VerificationMode mode) 的返回值的方法调用,验证某些行为是否至少发生过一次/确切的次数/从未发生过。

方法调用次数校验说明times(int wantedNumberOfInvocations)允许验证调用的确切次数。atLeast(int minNumberOfInvocations)允许至少 x 次调用的验证。atMost(int maxNumberOfInvocations)允许最多 x 次调用的验证。atLeastOnce()允许至少一次调用的验证。atMostOnce()允许最多一次调用的验证。never()times(0)的别名,见 times(int) 。only()允许检查给定的方法是否只调用一次。类是否被调用校验说明verifyNoInteractions(Object... mocks)验证给定的 mock 对象上没有发生交互。verifyNoMoreInteractions(Object... mocks)检查任何给定的 mock 对象上是否有任何未经验证的交互。validateMockitoUsage()验证测试代码中是否有书写错误的地方。如是否有漏写 return、verifyMethod 等等。package org.example;

import org.example.service.UserService;

import org.junit.jupiter.api.Assertions;

import org.junit.jupiter.api.Test;

import org.junit.jupiter.api.extension.ExtendWith;

import org.junit.jupiter.api.function.Executable;

import org.mockito.InjectMocks;

import org.mockito.Mock;

import org.mockito.exceptions.misusing.UnfinishedVerificationException;

import org.mockito.exceptions.verification.NoInteractionsWanted;

import org.mockito.junit.jupiter.MockitoExtension;

import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)

public class VerifyTest {

@Mock

private UserService userService;

@InjectMocks

private MyTester myTester;

@Test

void test_verifyNoInteractions() {

verifyNoInteractions(userService); // 通过,因为无调用 userService

myTester.doSomething();

Assertions.assertThrowsExactly(NoInteractionsWanted.class, new Executable() {

@Override

public void execute() throws Throwable {

verifyNoInteractions(userService); // 异常,因为 myTester 内部调用 userService

}

});

verify(userService).getUserAll();

Assertions.assertThrowsExactly(NoInteractionsWanted.class, new Executable() {

@Override

public void execute() throws Throwable {

verifyNoInteractions(userService); // 异常,因为判断的是 “是否有被调用过”

}

});

}

@Test

void test_verifyNoMoreInteractions() {

verifyNoMoreInteractions(userService); // 通过,因为无调用 userService

myTester.doSomething();

Assertions.assertThrowsExactly(NoInteractionsWanted.class, new Executable() {

@Override

public void execute() throws Throwable {

verifyNoMoreInteractions(userService); // 异常,因为 myTester 内部调用 userService

}

});

verify(userService).getUserAll();

verifyNoMoreInteractions(userService); // 通过,因为判断的是 “是否没有更多异常” 了

}

@Test

void test_validateMockitoUsage() {

verify(userService);

Assertions.assertThrowsExactly(UnfinishedVerificationException.class, new Executable() {

@Override

public void execute() throws Throwable {

validateMockitoUsage(); // 异常,因为 verify 没有方法调用

}

});

}

private static class MyTester {

private UserService userService;

void doSomething() {

userService.getUserAll();

}

}

}

原理todo 原理

@Test

public void testInit() {

Mockito.mockingDetails(mockUserService).isMock(); // true

Mockito.mockingDetails(spyUserService).isSpy(); // true

Mockito.mockingDetails(spyUserService).isMock(); // true —— ❗Spy是Mock的子类

}

问题: mock FunctionInterface 输入todo 完善

package org.example;

import org.junit.jupiter.api.Test;

import org.mockito.MockedConstruction;

import org.mockito.invocation.InvocationOnMock;

import java.util.concurrent.atomic.AtomicInteger;

import java.util.function.Function;

import static org.junit.jupiter.api.Assertions.assertEquals;

import static org.junit.jupiter.api.Assertions.assertThrowsExactly;

import static org.mockito.Mockito.*;

public class Mock02FunctionInterfaceTest {

public static class FuncHandler {

private String pid;

private boolean flag = false;

public FuncHandler(String id) {

this.pid = "p_" + id;

}

public FuncHandler method_01() {

flag = true;

return this;

}

public T method_02(Function func) {

if (!flag) {

throw new UnsupportedOperationException("please run method 01 first");

}

return func.apply(pid);

}

}

@Test

void testHandler() {

assertEquals("xx_p_" + "world", new FuncHandler("world").method_01().method_02(pid -> "xx_" + pid));

assertThrowsExactly(UnsupportedOperationException.class, () -> new FuncHandler("world").method_02(pid -> "xx_" + pid));

}

@Test

void testHandlerMock() {

try (MockedConstruction funcHandlerMockedConstruction = mockConstruction(FuncHandler.class, new MockedConstruction.MockInitializer() {

final private AtomicInteger i = new AtomicInteger(0);

private Object answer(InvocationOnMock invocation) throws Throwable {

if (i.getAndIncrement() == 0) {

Function arg0 = invocation.getArgument(0);

return arg0.apply("p_world");

} else {

return invocation.callRealMethod();

}

}

@Override

public void prepare(FuncHandler mock, MockedConstruction.Context context) throws Throwable {

when(mock.method_01()).thenCallRealMethod();

doAnswer(this::answer).when(mock).method_02(any());

}

})) {

testHandler();

}

}

}