文章目录

  • 一、写在前面
    • 1、简介
    • 2、依赖
  • 二、使用
    • 1、基本使用
    • 2、注解
      • (1)开启注解
      • (2)@Mock 注解
      • (3)@DoNotMock 注解
      • (4)@Spy 注解
      • (5)@Captor 注解
      • (6)@InjectMocks 注解
      • (7)将Mock注入Spy中
      • (8)使用注解时遇到空指针
    • 3、Mockito模拟抛出异常
      • (1)非Void返回值
      • (2)Void返回值
      • (3)异常作为对象
      • (4)模拟对象(spy)
    • 4、When/Then 用法
    • 5、Mockito Verify用法
    • 6、Mockito模拟返回值为void的方法
      • (1)简单的模拟与验证
      • (2)参数捕获
      • (3)回答对void的调用
      • (4)部分模拟
    • 7、Mockito模拟final类和方法
    • 9、Mockito模拟静态方法
      • (1)Mock 无参静态方法
      • (2)mock带有参数的静态方法
      • (3)解决MockitoException:Deregistering Existing Mock Registrations
  • 三、Spring中使用Mockito
    • 1、Spring中使用Mockito案例

一、写在前面

1、简介

参考资料:https://www.baeldung-cn.com/mockito-series

在进行单元测试时,如果依赖的服务尚未开发完成,或依赖的对象不方便构造,这时我们就需要模拟( Mock)对象。

2、依赖

如果是普通java项目,需要引入mockito-core,对于 Spring Boot 用户,spring-boot-starter-test 中已经集成好了Mockito,无需配置。

<dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>3.12.4</version><scope>test</scope>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId>
</dependency>

二、使用

1、基本使用

import static org.mockito.Mockito.*;// 创建mock对象 
// 你可以mock具体的类型,不仅只是接口
List mockedList = mock(List.class);
// 对于高版本Mockito 4.10.0+,可以写的更简洁
// List mockedList = mock();// 下面添加测试桩(stubbing),指定mock的行为
// ”当“ 调用 mockedList.get(0) 返回 "first"
when(mockedList.get(0)).thenReturn("first");// 下面代码将打印 "first"
System.out.println(mockedList.get(0));// 下面将打印 "null",因为 get(999) 没有被打桩
System.out.println(mockedList.get(999));

2、注解

(1)开启注解

开启注解有三种方式:

//方法一:在JUnit 上设置 MockitoJUnitRunner
@ExtendWith(MockitoExtension.class)
public class MockitoAnnotationUnitTest {...
}// 方法二:手动编码,调用 MockitoAnnotations.openMocks() 方法
@Before
public void init() {MockitoAnnotations.openMocks(this);
}//最后, 我们可以使用 MockitoJUnit.rule():
public class MockitoAnnotationsInitWithMockitoJUnitRuleUnitTest {//注意,这需要将rule 设置为 public@Rulepublic MockitoRule initRule = MockitoJUnit.rule();...
}

(2)@Mock 注解

@Mock 是 Mockito 中用的最多的注解,我们用它来创建并注入mock对象,而不用手动调用 Mockito.mock 方法。

@Test
public void whenNotUseMockAnnotation_thenCorrect() {List mockList = Mockito.mock(ArrayList.class);mockList.add("one");Mockito.verify(mockList).add("one");assertEquals(0, mockList.size());Mockito.when(mockList.size()).thenReturn(100);assertEquals(100, mockList.size());
}

对比一下,@Mock 注解可以完成以上编码的工作。

@Mock
List<String> mockedList;@Test
public void whenUseMockAnnotation_thenMockIsInjected() {mockedList.add("one");Mockito.verify(mockedList).add("one");assertEquals(0, mockedList.size());Mockito.when(mockedList.size()).thenReturn(100);assertEquals(100, mockedList.size());
}

(3)@DoNotMock 注解

@DoNotMock 注解用来标记不要mock的类或接口

import org.mockito.exceptions.misusing.DoNotMock;@DoNotMock(reason = "Use a real instance instead")
public abstract class NotToMock {// Class implementation
}

(4)@Spy 注解

spy与mock的区别是,mock代理了目标对象的全部方法,spy只是部分代理

我们先不用注解的方式,演示如何创建一个 spy List。

@Test
public void whenNotUseSpyAnnotation_thenCorrect() {// 需要声明一个对象List<String> spyList = Mockito.spy(new ArrayList<String>());// 走正常的ArrayList方法spyList.add("one");spyList.add("two");Mockito.verify(spyList).add("one");Mockito.verify(spyList).add("two");assertEquals(2, spyList.size());Mockito.doReturn(100).when(spyList).size();assertEquals(100, spyList.size());
}

然后我们通过 @Spy 注解的方式完成相同的工作:

@Spy
List<String> spiedList = new ArrayList<String>();@Test
public void whenUseSpyAnnotation_thenSpyIsInjectedCorrectly() {spiedList.add("one");spiedList.add("two");Mockito.verify(spiedList).add("one");Mockito.verify(spiedList).add("two");assertEquals(2, spiedList.size());Mockito.doReturn(100).when(spiedList).size();assertEquals(100, spiedList.size());
}

(5)@Captor 注解

ArgumentCaptor 让我们能够 “拦截” 方法调用的参数,从而对其进行验证,这在测试依赖于外部交互的代码时非常有用。
接下来让我们看看如何使用 @Captor 注解创建 ArgumentCaptor 实例。

在下面的示例中,我们先不使用 @Captor 注解,手动创建一个 ArgumentCaptor:

@Test
public void whenUseCaptorAnnotation_thenTheSame() {List mockList = Mockito.mock(List.class);ArgumentCaptor<String> arg = ArgumentCaptor.forClass(String.class);mockList.add("one");Mockito.verify(mockList).add(arg.capture());assertEquals("one", arg.getValue());
}

现在,让我们使用 @Captor 注解来创建 ArgumentCaptor:

@Mock
List mockedList;@Captor 
ArgumentCaptor argCaptor;@Test
public void whenUseCaptorAnnotation_thenTheSam() {mockedList.add("one");Mockito.verify(mockedList).add(argCaptor.capture());assertEquals("one", argCaptor.getValue());
}

(6)@InjectMocks 注解

现在我们来讨论如何使用 @InjectMocks 注解将mock字段自动注入到被测试对象中。

在下面的示例中,我们将使用 @InjectMocks 把mock的 wordMap 注入到 MyDictionary dic 中:

@Mock
Map<String, String> wordMap;@InjectMocks
MyDictionary dic = new MyDictionary();@Test
public void whenUseInjectMocksAnnotation_thenCorrect() {Mockito.when(wordMap.get("aWord")).thenReturn("aMeaning");assertEquals("aMeaning", dic.getMeaning("aWord"));
}

下面是 MyDictionary 类:

public class MyDictionary {Map<String, String> wordMap;public MyDictionary() {wordMap = new HashMap<String, String>();}public void add(final String word, final String meaning) {wordMap.put(word, meaning);}public String getMeaning(final String word) {return wordMap.get(word);}
}

(7)将Mock注入Spy中

与前面测试类似,我们可能想在spy中注入一个mock:

@Mock
Map<String, String> wordMap;@Spy
MyDictionary spyDic = new MyDictionary();

然而,Mockito 并不支持将mock注入spy,因此下面的测试会出现异常:

@Test 
public void whenUseInjectMocksAnnotation_thenCorrect() { Mockito.when(wordMap.get("aWord")).thenReturn("aMeaning"); assertEquals("aMeaning", spyDic.getMeaning("aWord")); 
}

如果我们想在 spy 中使用 mock,可以通过构造函数手动注入 mock:

MyDictionary(Map<String, String> wordMap) {this.wordMap = wordMap;
}

现在需要我们手动创建spy,而不使用注释:

@Mock
Map<String, String> wordMap; MyDictionary spyDic;@BeforeEach
public void init() {MockitoAnnotations.openMocks(this);spyDic = Mockito.spy(new MyDictionary(wordMap));
}

现在测试将通过。

(8)使用注解时遇到空指针

通常,当我们使用 @Mock 或 @Spy 注解时,可能会遇到 NullPointerException 异常:

public class MockitoAnnotationsUninitializedUnitTest {@MockList<String> mockedList;@Test(expected = NullPointerException.class)public void whenMockitoAnnotationsUninitialized_thenNPEThrown() {Mockito.when(mockedList.size()).thenReturn(1);}
}

大多数情况下,是因为我们没有启用 Mockito 注解。所以请查看我们第一节的内容,使用Mockito前别忘了先初始化。

3、Mockito模拟抛出异常

测试类:

class MyDictionary {private Map<String, String> wordMap;public void add(String word, String meaning) {wordMap.put(word, meaning);}public String getMeaning(String word) {return wordMap.get(word);}
}

(1)非Void返回值

首先,如果方法的返回类型不是void,我们可以使用when().thenThrow():

@Test
void givenNonVoidReturnType_whenUsingWhenThen_thenExceptionIsThrown() {MyDictionary dictMock = mock(MyDictionary.class);when(dictMock.getMeaning(anyString())).thenThrow(NullPointerException.class);assertThrows(NullPointerException.class, () -> dictMock.getMeaning("word"));
}

请注意,我们已配置了返回类型为String的getMeaning()方法,使其在被调用时抛出NullPointerException。

(2)Void返回值

如果我们的方法返回void,我们将使用doThrow():

@Test
void givenVoidReturnType_whenUsingDoThrow_thenExceptionIsThrown() {MyDictionary dictMock = mock(MyDictionary.class);doThrow(IllegalStateException.class).when(dictMock).add(anyString(), anyString());assertThrows(IllegalStateException.class, () -> dictMock.add("word", "meaning"));
}

在这里,我们配置了一个返回void的add()方法,在调用时抛出IllegalStateException。

对于void返回类型,我们不能使用when().thenThrow(),因为编译器不允许在括号内使用void方法。

(3)异常作为对象

为了配置异常本身,我们可以像之前示例那样传递异常的类,也可以作为对象:

@Test
void givenNonVoidReturnType_whenUsingWhenThenAndExeceptionAsNewObject_thenExceptionIsThrown() {MyDictionary dictMock = mock(MyDictionary.class);when(dictMock.getMeaning(anyString())).thenThrow(new NullPointerException("Error occurred"));assertThrows(NullPointerException.class, () -> dictMock.getMeaning("word"));
}

对于doThrow(),我们也可以这样做:

@Test
void givenNonVoidReturnType_whenUsingDoThrowAndExeceptionAsNewObject_thenExceptionIsThrown() {MyDictionary dictMock = mock(MyDictionary.class);doThrow(new IllegalStateException("Error occurred")).when(dictMock).add(anyString(), anyString());assertThrows(IllegalStateException.class, () -> dictMock.add("word", "meaning"));
}

(4)模拟对象(spy)

我们还可以以与mock相同的方式为模拟对象(Spy)配置抛出异常:

@Test
void givenSpyAndNonVoidReturnType_whenUsingWhenThen_thenExceptionIsThrown() {MyDictionary dict = new MyDictionary();MyDictionary spy = Mockito.spy(dict);when(spy.getMeaning(anyString())).thenThrow(NullPointerException.class);assertThrows(NullPointerException.class, () -> spy.getMeaning("word"));
}

4、When/Then 用法

测试类:

public class MyList extends AbstractList<String> {@Overridepublic String get(final int index) {return null;}@Overridepublic int size() {return 1;}
}

为mock配置简单返回行为:

MyList listMock = mock(MyList.class);
when(listMock.add(anyString())).thenReturn(false);boolean added = listMock.add(randomAlphabetic(6));
assertThat(added).isFalse();

以另一种方式为mock配置返回行为:

MyList listMock = mock(MyList.class);
doReturn(false).when(listMock).add(anyString());boolean added = listMock.add(randomAlphabetic(6));
assertThat(added).isFalse();

配置mock在方法调用时抛出异常:

MyList listMock = mock(MyList.class);
when(listMock.add(anyString())).thenThrow(IllegalStateException.class);assertThrows(IllegalStateException.class, () -> listMock.add(randomAlphabetic(6)));

配置具有void返回类型的方法的行为——抛出异常:

MyList listMock = mock(MyList.class);
doThrow(NullPointerException.class).when(listMock).clear();assertThrows(NullPointerException.class, () -> listMock.clear());

配置多次调用的行为:

MyList listMock = mock(MyList.class);
when(listMock.add(anyString())).thenReturn(false).thenThrow(IllegalStateException.class);assertThrows(IllegalStateException.class, () -> {listMock.add(randomAlphabetic(6));listMock.add(randomAlphabetic(6));
});

配置spy的行为:

MyList instance = new MyList();
MyList spy = spy(instance);doThrow(NullPointerException.class).when(spy).size();assertThrows(NullPointerException.class, () -> spy.size());

配置mock调用实际底层方法的行为:

MyList listMock = mock(MyList.class);
when(listMock.size()).thenCallRealMethod();assertThat(listMock).hasSize(1);

配置mock方法调用自定义Answer:

MyList listMock = mock(MyList.class);
doAnswer(invocation -> "Always the same").when(listMock).get(anyInt());String element = listMock.get(1);
assertThat(element).isEqualTo("Always the same");

5、Mockito Verify用法

在 Mockito verify 用于验证某个方法是否被调用,以及调用的次数和参数。本文我们 通过示例演示 Mockito verify 的各种用法。

下面是我们将要 mock 的 List:

public class MyList extends AbstractList<String> {@Overridepublic String get(final int index) {return null;}@Overridepublic int size() {return 1;}
}

简单验证:

List<String> mockedList = mock(MyList.class);
mockedList.size();// 验证 size() 方法是否被调用
verify(mockedList).size();

验证mock的调用次数:

List<String> mockedList = mock(MyList.class);
mockedList.size();
// 验证 size() 方法是否被调用了 1 次
verify(mockedList, times(1)).size();

验证 mock 对象的所有方法都没有被调用:

List<String> mockedList = mock(MyList.class);
verifyNoInteractions(mockedList);

验证 mock 的某个方法被调用:

List<String> mockedList = mock(MyList.class);
verify(mockedList, times(0)).size();

验证没有额外的调用:

List<String> mockedList = mock(MyList.class);
mockedList.size();
mockedList.clear();verify(mockedList).size();
// 除了 size() 外,clear()也被调用了,所以下面会抛出异常
assertThrows(NoInteractionsWanted.class, () -> verifyNoMoreInteractions(mockedList));

验证调用顺序:

List<String> mockedList = mock(MyList.class);
mockedList.size();
mockedList.add("a parameter");
mockedList.clear();InOrder inOrder = Mockito.inOrder(mockedList);
inOrder.verify(mockedList).size();
inOrder.verify(mockedList).add("a parameter");
inOrder.verify(mockedList).clear();

验证没有调用某个方法:

List<String> mockedList = mock(MyList.class);
mockedList.size();verify(mockedList, never()).clear();

验证至少调用次数:

List<String> mockedList = mock(MyList.class);
mockedList.clear();
mockedList.clear();
mockedList.clear();verify(mockedList, atLeast(1)).clear();
verify(mockedList, atMost(10)).clear();

验证调用时实际传入的参数:

List<String> mockedList = mock(MyList.class);
mockedList.add("test");verify(mockedList).add("test");

验证调用时传入的参数,不关心具体值:

List<String> mockedList = mock(MyList.class);
mockedList.add("test");verify(mockedList).add(anyString());

验证调用时传入的参数,并捕获参数:

List<String> mockedList = mock(MyList.class);
mockedList.addAll(Lists.<String> newArrayList("someElement"));ArgumentCaptor<List<String>> argumentCaptor = ArgumentCaptor.forClass(List.class);
verify(mockedList).addAll(argumentCaptor.capture());List<String> capturedArgument = argumentCaptor.getValue();
assertThat(capturedArgument).contains("someElement");

6、Mockito模拟返回值为void的方法

测试类:

public class MyList extends AbstractList<String> {@Overridepublic void add(int index, String element) {// no-op}
}

(1)简单的模拟与验证

void方法可以与Mockito的doNothing()、doThrow()和doAnswer()方法一起使用,使模拟和验证变得直观:

@Test
public void whenAddCalled_thenVerified() {MyList myList = mock(MyList.class);doNothing().when(myList).add(isA(Integer.class), isA(String.class));myList.add(0, "");verify(myList, times(1)).add(0, "");
}

然而,doNothing()是Mockito对void方法的默认行为。

这个版本的whenAddCalledVerified()与上面的实现相同:

@Test
void whenAddCalled_thenVerified() {MyList myList = mock(MyList.class);myList.add(0, "");verify(myList, times(1)).add(0, "");
}

doThrow()会抛出一个异常:

@Test
void givenNull_whenAddCalled_thenThrowsException() {MyList myList = mock(MyList.class);assertThrows(Exception.class, () -> {doThrow().when(myList).add(isA(Integer.class), isNull());});myList.add(0, null);
}

我们将在下面讨论doAnswer()。

(2)参数捕获

使用doNothing()覆盖默认行为的一个原因是捕获参数。

在上述例子中,我们使用verify()方法检查传递给add()的方法参数。

然而,我们可能需要捕获参数并对其做更多的处理。

在这种情况下,我们像上面那样使用doNothing(),但使用一个ArgumentCaptor:

@Test
void givenArgumentCaptor_whenAddCalled_thenValueCaptured() {MyList myList = mock(MyList.class);ArgumentCaptor<String> valueCapture = ArgumentCaptor.forClass(String.class);doNothing().when(myList).add(any(Integer.class), valueCapture.capture());myList.add(0, "captured");assertEquals("captured", valueCapture.getValue());
}

(3)回答对void的调用

一个方法可能执行的不仅仅是添加或设置值的简单行为。

对于这些情况,我们可以使用Mockito的Answer来添加我们需要的行为:

@Test
void givenDoAnswer_whenAddCalled_thenAnswered() {MyList myList = mock(MyList.class);doAnswer(invocation -> {Object arg0 = invocation.getArgument(0);Object arg1 = invocation.getArgument(1);assertEquals(3, arg0);assertEquals("answer me", arg1);return null;}).when(myList).add(any(Integer.class), any(String.class));myList.add(3, "answer me");
}

如Mockito的Java 8特性所述,我们使用带有Answer的lambda来为add()定义自定义行为。

(4)部分模拟

部分模拟也是一个选择。Mockito的doCallRealMethod()也可以用于void方法:

@Test
void givenDoCallRealMethod_whenAddCalled_thenRealMethodCalled() {MyList myList = mock(MyList.class);doCallRealMethod().when(myList).add(any(Integer.class), any(String.class));myList.add(1, "real");verify(myList, times(1)).add(1, "real");
}

这样,我们可以在同时调用实际方法并进行验证。

7、Mockito模拟final类和方法

在早期版本的 Mockito(3.0 之前),默认是不支持模拟 final 类、final 方法和静态方法的,因为 Java 的 final 修饰符会限制字节码修改,而 Mockito 传统上依赖 CGLIB 或 Javassist 等库通过生成子类的方式创建 mock 对象,final 元素会阻止这种子类生成。

但从 Mockito 3.0 版本开始,通过结合 Byte Buddy 字节码操作库和 Java Agent 技术,实现了对 final 类、final 方法及静态方法的模拟支持。

测试类:

public class MyList extends AbstractList<String> {final public int finalMethod() {return 0;}
}
public final class FinalList extends MyList {@Overridepublic int size() {return 1;}
}

用法没有什么不同,

@Test
public void whenMockFinalMethod_thenMockWorks() {MyList myList = new MyList();MyList mock = mock(MyList.class);when(mock.finalMethod()).thenReturn(1);assertThat(mock.finalMethod()).isNotZero();
}
@Test
public void whenMockFinalClass_thenMockWorks() {FinalList mock = mock(FinalList.class);when(mock.size()).thenReturn(2);assertThat(mock.size()).isNotEqualTo(1);
}

9、Mockito模拟静态方法

Mockito 3.4.0版本之前,是不支持直接mock静态方法,需要借助于PowerMockito。

有人可能会说,在编写整洁(Clean Code)的面向对象 代码时,我们不应该需要模拟静态类。这通常暗示了我们的应用存在设计问题。
为什么呢?首先,依赖于静态方法的类具有紧密的耦合性,其次,它几乎总是导致难以测试的代码。理想情况下,一个类不应当负责获取其依赖项,如果可能的话,它们应该由外部注入。

测试类:

public class StaticUtils {private StaticUtils() {}public static List<Integer> range(int start, int end) {return IntStream.range(start, end).boxed().collect(Collectors.toList());}public static String name() {return "Baeldung";}
}

(1)Mock 无参静态方法

Mockito 3.4.0版本后,我们可以使用 Mockito.mockStatic(Class classToMock) 来mock静态方法的调用。 其返回值是一个MockedStatic类型的模拟对象。

注意返回的是一个 scoped mock object,它只在当前线程(thread-local)作用域内有效,用完需要close模拟对象,这就是为什么我们使用 try-with-resources,MockedStatic 继承了 AutoCloseable接口。

@Test
void givenStaticMethodWithNoArgs_whenMocked_thenReturnsMockSuccessfully() {assertThat(StaticUtils.name()).isEqualTo("Baeldung");try (MockedStatic<StaticUtils> utilities = Mockito.mockStatic(StaticUtils.class)) {// 模拟 StaticUtils.name()方法utilities.when(StaticUtils::name).thenReturn("Eugen");assertThat(StaticUtils.name()).isEqualTo("Eugen");}// 离开mock作用域后调用的是真实的方法assertThat(StaticUtils.name()).isEqualTo("Baeldung");
}

(2)mock带有参数的静态方法

用法和无参静态方法类似,除了需要指定模拟的参数。

@Test
void givenStaticMethodWithArgs_whenMocked_thenReturnsMockSuccessfully() {assertThat(StaticUtils.range(2, 6)).containsExactly(2, 3, 4, 5);try (MockedStatic<StaticUtils> utilities = Mockito.mockStatic(StaticUtils.class)) {// mock `StaticUtils.range()` 方法,它有2个参数:utilities.when(() -> StaticUtils.range(2, 6)).thenReturn(Arrays.asList(10, 11, 12));assertThat(StaticUtils.range(2, 6)).containsExactly(10, 11, 12);}assertThat(StaticUtils.range(2, 6)).containsExactly(2, 3, 4, 5);
}

(3)解决MockitoException:Deregistering Existing Mock Registrations

在Java中,当试图在同一线程上下文中注册多个静态模拟时,通常会出现 “静态模拟已经在当前线程中注册” 的异常,违反了单次注册约束。 要解决这个问题,我们必须在创建新模拟之前先注销现有的静态模拟。

简单来说,我们需要:

在每个线程中为静态模拟注册一次,最好使用如@Before这样的设置方法。
在注册之前检查mock是否已经注册以防止冗余。
在使用@After注册新的静态模拟之前,请先取消注册同一类的所有现有模拟。
以下是如何处理 “static mocking is already registered in the current thread” 异常的完整示例:

public class StaticMockRegistrationUnitTest {private MockedStatic<StaticUtils> mockStatic;@Beforepublic void setUp() {// Registering a static mock for UserService before each testmockStatic = mockStatic(StaticUtils.class);}@Afterpublic void tearDown() {// Closing the mockStatic after each testmockStatic.close();}@Testpublic void givenStaticMockRegistration_whenMocked_thenReturnsMockSuccessfully() {// Ensure that the static mock for UserService is registeredassertTrue(Mockito.mockingDetails(StaticUtils.class).isMock());}@Testpublic void givenAnotherStaticMockRegistration_whenMocked_thenReturnsMockSuccessfully() {// Ensure that the static mock for UserService is registeredassertTrue(Mockito.mockingDetails(StaticUtils.class).isMock());}
}

在上述示例中,带有 @Before 注解的 setUp() 方法会在每个测试用例之前执行,确保一致的测试环境。在这个方法中,使用 mockStatic(StaticUtils.class) 为 StaticUtils 注册静态模拟。这个注册过程确保每个测试前都会实例化一个新的静态模拟,保持测试的独立性,防止测试用例之间相互干扰。

相反,@After 注解的 tearDown() 方法会在每个测试用例后执行,释放测试执行期间获取的所有资源。

这个细致的设置和清理流程确保每个测试用例在其控制的环境中运行,促进可靠和可重现的测试结果,同时遵循单元测试的最佳实践。

三、Spring中使用Mockito

1、Spring中使用Mockito案例

首先,我们必须配置测试的应用上下文:

@Profile("test")
@Configuration
public class NameServiceTestConfiguration {@Bean@Primarypublic NameService nameService() {return Mockito.mock(NameService.class);}
}

@Profile注解告诉Spring只有在“test”配置活跃时才应用此配置。@Primary注解确保在自动装配时使用这个实例而不是真实实例。方法本身创建并返回我们的NameService类的Mockito模拟。

接下来我们可以编写单元测试:

@ActiveProfiles("test")
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MocksApplication.class)
public class UserServiceUnitTest {@Autowiredprivate UserService userService;@Autowiredprivate NameService nameService;@Testpublic void whenUserIdIsProvided_thenRetrievedNameIsCorrect() {Mockito.when(nameService.getUserName("SomeId")).thenReturn("Mock user name");String testName = userService.getUserName("SomeId");Assert.assertEquals("Mock user name", testName);}
}

我们使用@ActiveProfiles注解启用“test”配置,并激活我们之前编写的模拟配置。结果,Spring为UserService类自动装配一个真实实例,但对于NameService类则是模拟对象。测试本身是一个典型的JUnit+Mockito测试。我们配置模拟对象的行为,然后调用我们想要测试的方法,并断言其返回我们期望的值。

也可以(尽管不推荐)避免在这样的测试中使用环境配置。要做到这一点,可以移除@Profile和@ActiveProfiles注解,并在UserServiceTest类上添加@ContextConfiguration(classes = NameServiceTestConfiguration.class)注解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/93695.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/93695.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/93695.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

群晖Synology Drive:打造高效安全的私有云协作平台

随着企业与个人对数据协作、安全与自主性的需求不断提升&#xff0c;群晖&#xff08;Synology&#xff09;推出的 Synology Drive 成为了私人云存储与团队协作的利器。下面将从功能亮点、使用方式、安全管理、适用场景等角度&#xff0c;为你全面解读这款强大的私有云方案。Sy…

开发避坑短篇(11):Oracle DATE(7)到MySQL时间类型精度冲突解决方案

异常信息 [Err] [Dtf] 1426 - Too-big precision 7 specified for CREATE_TIME. Maximum is 6.异常背景 用Navicat的数据传输功能进行oracle的数据表迁移到到mysql时报错。 异常分析 oracle的DATE类型的长度是7位&#xff0c;而mysql的datetime类型的长度最多6位&#xff0c;所…

怎么判断一个DAPP是否真正去中心化

判断一个DAPP&#xff08;去中心化应用&#xff09;是否真正去中心化&#xff0c;需要从多个维度进行考察。以下是关键评估标准&#xff1a;1. 区块链依赖程度✅ 真正去中心化&#xff1a;核心逻辑和数据处理完全依赖智能合约&#xff0c;运行在区块链上&#xff08;如以太坊、…

F12 开发者工具 使用指北

F12 开发者工具 使用指北元素 Elements控制台 Console源代码 Sources网络 Network请求文件具体说明首先介绍Chrome开发者工具中&#xff0c;调试时使用最多的三个功能页面是&#xff1a;元素&#xff08;ELements&#xff09;、控制台&#xff08;Console&#xff09;、源代码&…

AD域设计与管理-域策略-进阶

AD域安全保密要求&#xff0c;也是最为常见的一些组策略配置需求 目录 1.禁止U盘&#xff0c;DVD&#xff0c;软盘等可移动存储使用 2.禁止员工自行安装软件 3.硬盘全部采用bitlocker上锁&#xff0c;密码保存至AD域控 4.密码复杂度要求 5.开启windows防火墙且不允许员工…

Python设计模式详解:策略模式(Strategy Pattern)实战指南

Python设计模式详解&#xff1a;策略模式实战指南什么是策略模式&#xff1f;核心组件基础实现利用Python特性的高级实现使用装饰器的策略模式策略模式的优势策略模式的适用场景实际应用案例&#xff1a;电商折扣系统注意事项总结在面向对象编程中&#xff0c;设计模式为常见问…

一次 web 请求响应中,通常那个部分最耗时?

文章目录一次Web请求的完整旅程1. DNS解析2. TCP连接建立3. 发送HTTP请求4. 服务器处理5. 服务器响应6. 浏览器渲染哪个环节通常最耗时&#xff1f;1. 数据库查询2. 外部API调用3. 复杂的业务逻辑如何优化各个环节&#xff1f;1. 数据库优化2. 缓存策略3. 异步处理总结一次Web请…

IO流-概述和体系

1.什么是I0流?存储和读取数据的解决方案|: input 0: output流:像水流一样传输数据2.10流的作用?用于读写数据(本地文件&#xff0c;网络)3. I0流按照流向可以分类哪两种流?输出流:程序-->文件输入流:文件-->程序4. I0流按照操作文件的类型可以分类哪两种流?…

提高建筑舒适度与能源效率,楼宇自控系统意义重大

随着城市化进程的加速和人们对建筑环境要求的不断提高&#xff0c;如何在保证建筑舒适度的同时提升能源效率&#xff0c;成为建筑行业面临的重要课题。楼宇自控系统&#xff08;Building Automation System&#xff0c;简称BAS&#xff09;作为现代智能建筑的核心组成部分&…

学习笔记《区块链技术与应用》第4天 比特币脚本语言

输入0.7 输出0.5 23个确认 不太可能回滚了交易id hash值 版本 locktime 交易剩下时间&#xff1a;0立即生效 confirmation:确认信息 time&#xff1a;产生时间 blocktime&#xff1a;块产生时间vout: 交易中第0个输入 scriptSig&#xff1a;输入脚本&#xff08;input script)n…

3.Linux 系统文件类型与文件权限

1.文件类型Linux 下所有的东西都可以看做文件&#xff0c;Linux 将文件分为以下几种类型&#xff1a;普通文件 ‘-’目录文件 ‘d’管道文件 ‘p’链接文件 ‘l’设备文件&#xff08;块设备 ’b’ 、字符设备 ‘c’&#xff09;套接字文件 ‘s’Linux 上不以文件的扩展名区别文…

订单识别技术原理及场景应用

订单OCR&#xff08;光学字符识别&#xff09;技术通过图像处理和深度学习算法&#xff0c;将纸质或电子版订单中的文字信息转化为结构化数据。以下是其技术原理和典型应用场景的详细解析&#xff1a;一、技术原理剖析1. 核心处理流程图像预处理去噪&#xff1a;消除阴影、折痕…

[优选算法]复写零

题目链接 LeetCode复写零 题目描述 题目解析 一、问题理解 题目要求&#xff1a;给定一个整数数组 arr&#xff0c;在不创建新数组的情况下&#xff0c;将每个出现的 0 复写一遍&#xff08;即一个 0 变成两个 0&#xff09;&#xff0c;同时保持其他元素的相对顺序不变。复…

element UI的el-table组件,实现可以拖动

表格 <div class"main_table"><el-table id"elTableid" :data"fieldArr" border style"width: 100%" row-class-name"drag-row"current-row-key highlight-current-row><el-table-column type"index&qu…

Android Emoji 全面解析:从使用到自定义

引言 Emoji已经成为现代数字通信不可或缺的一部分&#xff0c;这些小小的图标能够跨越语言障碍&#xff0c;直观地表达情感和想法。在Android开发中&#xff0c;正确处理和显示Emoji是提升用户体验的重要环节。本文将全面介绍Android平台上的Emoji支持&#xff0c;包括系统集成…

数据中心入门学习(五):服务器CPU

目录CPU1 概述1.1 概念1.2 冯诺依曼架构1.3 常见参数&#xff08;评估性能&#xff09;1.4 按指令集分类2 CPU发展2.1 发展史2.2 行业产业链2.3 英特尔 Xeon 至强处理器2.4 AMD Zen架构补充1 寄存器、存储器、内存、缓存、硬盘区别与联系&#xff1f;2 浮点单元参考本篇记录和梳…

基于MySQL实现基础图数据库

基于MySQL实现基础图数据库 一、概念 图数据库是一种用于存储和查询具有复杂关系的数据的数据库。在这种数据库中&#xff0c;数据被表示为节点&#xff08;实体&#xff09;和边&#xff08;关系&#xff09;。图数据库的核心优势在于能够快速地查询和处理节点之间的关系。 图…

RAG面试内容整理-9. 查询改写与增强(Query Rewriting, Query Expansion)

查询改写和查询增强是两种提升检索效果的技术,目标是在不改变用户意图的前提下,使检索器收到的查询更全面或明确,从而找到更多相关信息。 查询改写通常指将原始查询转换成语义等价但更明晰的形式。上一节谈到的对话查询改写是一个典型场景。在一般情况下,查询改写也适用于澄…

golang设置http代理

问题场景&#xff1a; golang通过eino的官方agent示例调用duckduckgo进行联网搜索时出现网络问题&#xff0c;电脑此时是挂了工具的浏览器整出打开 官方示例&#xff1a;https://www.cloudwego.io/zh/docs/eino/quick_start/agent_llm_with_tools/ 问题原因&#xff1a;go代码没…

Elasticsearch 现在默认启用 BBQ,并通过 ACORN 实现过滤向量搜索

作者&#xff1a;来自 Elastic Gilad Gal 探索 Elasticsearch 的向量搜索如何以更快的速度、更低的成本提供更优结果。 试用向量搜索&#xff1a;使用这套自定进度的 Search AI 实操学习课程&#xff0c;亲自体验向量搜索。你可以开始免费云试用&#xff0c;或立即在本地机器上…