Mockito:Java單元測試Mock框架

文章目錄

  • 一、寫在前面
    • 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;或立即在本地機器上…