Mockito+Junit5單元測試


參考:
https://segmentfault.com/a/1190000006746409
https://waylau.com/mockito-quick-start/

1.引入依賴

下面這個最新版本匹配似乎有問題

    testCompile group: 'org.mockito', name: 'mockito-core', version: '3.7.7'
    testCompile group: 'org.mockito', name: 'mockito-junit-jupiter', version: '3.7.7'
    testCompile group: 'org.junit.jupiter', name: 'junit-jupiter', version: '5.7.0'

改用下面的舊版本測試成功

    testCompile group: 'org.mockito', name: 'mockito-core', version: '2.18.3'
    testCompile group: 'org.mockito', name: 'mockito-junit-jupiter', version: '2.18.3'
    testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.2.0'

2.編寫測試用例

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mockito;

import java.util.*;

/**
 * @author zhengqian
 * @date 2021.01.22
 */
public class CommonTest {

    @Test
    public void createMockObject() {
        // 使用 mock 靜態方法創建 Mock 對象.
        List mockedList = Mockito.mock(List.class);
        Assertions.assertTrue(mockedList instanceof List);

        // mock 方法不僅可以 Mock 接口類, 還可以 Mock 具體的類型.
        ArrayList mockedArrayList = Mockito.mock(ArrayList.class);
        Assertions.assertTrue(mockedArrayList instanceof List);
        Assertions.assertTrue(mockedArrayList instanceof ArrayList);
    }

    @Test
    public void configMockObject() {
        List mockedList = Mockito.mock(List.class);

        // 我們定制了當調用 mockedList.add("one") 時, 返回 true
        Mockito.when(mockedList.add("one")).thenReturn(true);
        // 當調用 mockedList.size() 時, 返回 1
        Mockito.when(mockedList.size()).thenReturn(1);

        Assertions.assertTrue(mockedList.add("one"));
        // 因為我們沒有定制 add("two"), 因此返回默認值, 即 false.
        Assertions.assertFalse(mockedList.add("two"));
        Assertions.assertEquals(mockedList.size(), 1);

        Iterator i = Mockito.mock(Iterator.class);
        Mockito.when(i.next()).thenReturn("Hello,").thenReturn("Mockito!");
        String result = i.next() + " " + i.next();
        //assert
        Assertions.assertEquals("Hello, Mockito!", result);
    }

    @Test
    public void testVerify() {
        List mockedList = Mockito.mock(List.class);
        mockedList.add("one");
        mockedList.add("two");
        mockedList.add("three times");
        mockedList.add("three times");
        mockedList.add("three times");
        Mockito.when(mockedList.size()).thenReturn(5);
        Assertions.assertEquals(mockedList.size(), 5);

        // mockedList.add("one") 至少被調用了 1 次(atLeastOnce)
        Mockito.verify(mockedList, Mockito.atLeastOnce()).add("one");
        // mockedList.add("two") 被調用了 1 次(times(1))
        Mockito.verify(mockedList, Mockito.times(1)).add("two");
        // mockedList.add("three times") 被調用了 3 次(times(3))
        Mockito.verify(mockedList, Mockito.times(3)).add("three times");
        // mockedList.isEmpty() 從未被調用(never)
        Mockito.verify(mockedList, Mockito.never()).isEmpty();
    }

    @Test
    public void testSpy() {
        List list = new LinkedList();
        // 使用 spy() 部分模擬對象
        List spy = Mockito.spy(list);

        // 對 spy.size() 進行定制.
        Mockito.when(spy.size()).thenReturn(100);

        spy.add("one");
        spy.add("two");

        // 因為我們沒有對 get(0), get(1) 方法進行定制,
        // 因此這些調用其實是調用的真實對象的方法.
        Assertions.assertEquals(spy.get(0), "one");
        Assertions.assertEquals(spy.get(1), "two");

        Assertions.assertEquals(spy.size(), 100);
    }

    @Test
    public void testCaptureArgument() {
        List<String> list = Arrays.asList("1", "2");
        List mockedList = Mockito.mock(List.class);
        ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
        mockedList.addAll(list);
        // 獲取 mockedList.addAll 方法所傳遞的實參 list.
        Mockito.verify(mockedList).addAll(argument.capture());

        Assertions.assertEquals(2, argument.getValue().size());
        Assertions.assertEquals(list, argument.getValue());
    }

    @Test
    public void testArgumentMatcher() {
        List mockedList = Mockito.mock(List.class);
        //stubbing using built-in anyInt() argument matcher
        Mockito.when(mockedList.get(Mockito.anyInt())).thenReturn("element");

        //stubbing using custom matcher (let's say isValid() returns your own matcher implementation):
//        when(mockedList.contains(argThat(isValid()))).thenReturn("element");

        //following prints "element"
        System.out.println(mockedList.get(999));
    }

    @Test
    public void testThrowAndVoid() {
        List mockedList = Mockito.mock(List.class);
        Mockito.when(mockedList.get(1)).thenThrow(new RuntimeException());
        //following throws runtime exception
        System.out.println(mockedList.get(1));

        Mockito.doThrow(new RuntimeException()).when(mockedList).clear();
        //following throws RuntimeException:
        mockedList.clear();
    }

    @Test
    public void testInOrder() {
        // A. Single mock whose methods must be invoked in a particular order
        List singleMock = Mockito.mock(List.class);

        //using a single mock
        singleMock.add("was added first");
        singleMock.add("was added second");

        //create an inOrder verifier for a single mock
        InOrder inOrder = Mockito.inOrder(singleMock);

        //following will make sure that add is first called with "was added first, then with "was added second"
        inOrder.verify(singleMock).add("was added first");
        inOrder.verify(singleMock).add("was added second");

        // B. Multiple mocks that must be used in a particular order
        List firstMock = Mockito.mock(List.class);
        List secondMock = Mockito.mock(List.class);

        //using mocks
        firstMock.add("was called first");
        secondMock.add("was called second");

        //create inOrder object passing any mocks that need to be verified in order
        InOrder inOrder2 = Mockito.inOrder(firstMock, secondMock);

        //following will make sure that firstMock was called before secondMock
        inOrder2.verify(firstMock).add("was called first");
        inOrder2.verify(secondMock).add("was called second");

    }

    @Test
    public void testConsecutiveStubbing() {
        List mockedList = Mockito.mock(List.class);
        Mockito.when(mockedList.get(0))
                .thenThrow(new RuntimeException())
                .thenReturn("foo");
        // 精簡寫法
        // when(mockedList.get(0)).thenReturn("one", "two", "three");

        //First call: throws runtime exception:
        mockedList.get(0);

        //Second call: prints "foo"
        System.out.println(mockedList.get(0));

        //Any consecutive call: prints "foo" as well (last stubbing wins).
        System.out.println(mockedList.get(0));
    }

    @Test
    public void testCallbackStubbing() {
        List mockedList = Mockito.mock(List.class);
        Mockito.when(mockedList.get(Mockito.anyInt())).thenAnswer(invocation -> {
            Object[] args = invocation.getArguments();
            return "called with arguments: " + args;
        });

        //the following prints "called with arguments: foo"
        System.out.println(mockedList.get(111));
    }
}

使用mock注解

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.junit.jupiter.MockitoExtension;

/**
 * @author zhengqian
 * @date 2021.01.27
 */
@ExtendWith(MockitoExtension.class)
public class UserServiceTest {

    @Mock
    private UserService userService;

    @Test
    public void testGet() {
        Mockito.when(userService.get()).thenReturn("fake user");
        Assertions.assertEquals(userService.get(), "fake user");
    }
}

3. 其他JUnit5注解

@Test :表示方法是測試方法。但是與JUnit4的@Test不同,他的職責非常單一不能聲明任何屬性,拓展的測試將會由Jupiter提供額外測試

@ParameterizedTest :表示方法是參數化測試,下方會有詳細介紹

@RepeatedTest :表示方法可重復執行,下方會有詳細介紹

@DisplayName :為測試類或者測試方法設置展示名稱

@BeforeEach :表示在每個單元測試之前執行

@AfterEach :表示在每個單元測試之后執行

@BeforeAll :表示在所有單元測試之前執行

@AfterAll :表示在所有單元測試之后執行

@Tag :表示單元測試類別,類似於JUnit4中的@Categories

@Disabled :表示測試類或測試方法不執行,類似於JUnit4中的@Ignore

@Timeout :表示測試方法運行如果超過了指定時間將會返回錯誤

@ExtendWith :為測試類或測試方法提供擴展類引用

補充

如果運行test遇到以下錯誤:

FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':test'.
> No tests found for given includes: [org.example.mockito.demo.service.UserServiceTest.configMockObject](filter.includeTestsMatching)

改一下IDEA配置
圖片名稱


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM