日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

欧美企业必备技能-Mockito

發布時間:2023/12/20 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 欧美企业必备技能-Mockito 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

歐美企業做開發除了英語是必備技能之外,使用Mockito寫unit test(單位測試)
也是必備技能,畢業經歷的四家外企,美企,德企,德企,瑞士企業,沒有一家不寫UT的,中間在一個私企過渡了一下,呵呵,UT是什么?最近,小伙伴們在寫UT時遇到了各種問題,借此機會我對UT做一個總結,希望小伙伴們在寫UT時不要繞彎路。示例的源碼可以直接通過csdn下載也可以通過git導出:https://github.com/igdnss/mockito.git

注:這里主要是介紹Mockito的使用,所以需要有一定的junit test 的基礎,文中關于junit test 不懂的地方歡迎評論,私信,第一時間解答。

  • UnitTest 介紹
    • 常用標簽
  • Mockito介紹
    • 代碼介紹
    • mock對象
    • 深度mock對象
    • mock方法調用
      • 有返回值方法調用
      • 拋出異常方法調用
      • 無返回值方法調用
      • 迭代方法調用
      • thenAnswer處理業務邏輯
      • mock對象調用真實方法
      • spy 部分mock
    • mock參數匹配
      • isA()類型判斷
      • any類型判斷
      • 通配符的使用
    • hamcrest 的使用
  • 結束語

UnitTest 介紹

在介紹Mockito之前,強調Unit Test的幾點內容。

  • 支持自動化 (這里需要通過jenkins來支持) 。
  • 單元測試之間獨立不依賴 。
  • 單元測試獨立于數據庫,文件,或者其進程,一個好的單元測試完全獨立于外界資源。
  • 時間空間透明性,即不論何時何地執行,結果都應該一樣。
  • 單元測試需要具有一定的意義,不要存粹的去追求測試覆蓋率。
  • 單元測試追求簡短,且重要性等同于源代碼。
    其中第3天提到ut要獨立于外界資源,這看起來很奇怪,沒有外界資源ut怎么能跑起來,這就是本篇要要介紹的重點。沒有外界資源,我們需要偽造出資源,也就是業內所說mock資源。目前可以提供mock的框架有很多,例如easymock,powermock以及本文要介紹了mockito。
  • 常用標簽

    本篇主要介紹Mockito,順便將junit test中使用在方法上的標簽也整理出來,方便后面讀代碼。
    @BeforeClass
    在所有類執行之前執行,例如設置上下文環境,共享變量等。
    @Before
    在當前類執行之前執行,例如初始化對象,變量等。
    @After
    在當前類執行之后執行。
    @AfterClass
    在所有類執行之后執行
    @Test
    需要測試業務邏輯
    @Test(timeout=xxx)
    在timeout時間范圍內如果沒有執行完測試邏輯,則報錯
    @Test(expected=Exception.class)
    設置當前的測試邏輯拋出的異常,異常類型為Exception.class
    @Ignore
    忽略當前的測試邏輯,不執行

    Mockito介紹

    官網: https://site.mockto.org 不太喜歡這個官網,東西有點亂,但畢竟是官方的權威性,再亂也得硬著頭皮去看。mockito是一個測試框架,通過mock(模擬)外界資源讓測試代碼無需依賴真實環境就能跑起來,可以理解為讓測試代碼在虛擬環境中跑起來。接下來,我會將工作中常用到一些知識點全部整理出來,希望能幫助大家。依賴于自己寫的一個簡單三層架構的示例進行介紹,代碼的邏輯和環境很簡單,沒有具體的數據庫,沒有具體的業務邏輯,主要是為了介紹Mockito是如何將各個部分Mock出來并完成UT的。主要測試Controller層與Service層中的方法,有個別Mock的知識點可能沒有涉及到Demo中的方法,旨在介紹知識點。其中UnitTest和Mockito的版本分別為:4.13.2,1.10.19
    注意:mockito mock的資源不能是局部的,例如局總變量,私有方法是無法mock的。

    代碼介紹

    Controller 層:
    通過此層去調用業務層,實現根據id查找用戶的接口

    /***隨便寫的一個Controller,方便后面介紹UT而存在的*/ public class FindingController {UserService userService;//這里為了方便直接使用構造方法,真實項目中基本都是使用依賴注入的方式public FindingController(UserService userService) {this.userService = userService;}public String getUser(int id) {User user = null;try {user = userService.findById(id);} catch (UnsupportedOperationException e) {return "Error";}if(user == null) {return "No one";}else {return "Hello";}}public void clear() {userService.clear();}public List<User> getGroup() {return userService.getGroup();}public int getAge() {return userService.getAge();}}

    Mapper層:

    /*** 并沒有真正意義上去訪問數據庫*/ public class UserMapper {public User findById(int userId) {throw new UnsupportedOperationException();} }

    domain層:

    public class Base { } public class Client extends Base {} public class User extends Base{String name;public String getName() {return name;}public void setName(String name) {this.name = name;}}

    **service層: **

    public class UserService{UserMapper userMapper;public User findById(int userId) {User user = userMapper.findById(userId);return user;}public void clear() {throw new RuntimeException();}public List<User> getGroup() {throw new RuntimeException();}public int getAge() {return 18;}public int getAgeByIdAndName(int id,String name) {return 18;}public String getName(Base base) {throw new RuntimeException();}}

    以上代碼真的是一位含苞待放的姑娘,一切的一切都是那么的干凈清爽,在這些的代碼基礎之上我們對mockito進行抽絲剝繭。

    mock對象

    三種最常用的方法。

  • mock()
    使用此方法必須要在@RunWith(MockitoJUnitRunner.class)中傳入MockitoJUnitRunner.class 。這種法一般用于測試方法中局部對象的mock
    示例
  • @Beforepublic void setUp() {findingController = mock(FindingController.class);//這里使用的是mock方法}
  • @Mock
    可以使用@Mock標簽,一般mock全局對象。
    示例
  • @Mockprivate UserMapper userMapper;

    注:mock的對象只是一個空對象,其內部的屬性并沒有mock出來,所以直接使用mock的對象調用方法是會報空指針異常,因此使用mock的對象調用方法時,還需要mock方法的調用。或者在@Before中加上MockitoAnnotaion.initMocks(this);

    深度mock對象

    FindingController getUser()方法中調用了UserService的getUser()返回結果為User,如果這需要測試user.getName(),就得再需要mock一個User 對象。可以使用深度mock來解決這個問題。

    @Mock(answer=Answers.RETURNS_DEEP_STUBS)//深度mockprivate UserService userService;

    mock方法調用

    有返回值方法調用

    語法一: when(T methodCall).thenReturn(T returnValue);
    methodCall:方法調用;returnValue:調用此方法mock出的返回值。注:如果對象是mock出來的,必須要mock它的方法調用,否則會報空指針異常,因為mock的時候只mock一個空對象,其內部的屬性并沒有mock出來,所以報空指針異常。
    語法二: doReturn(Object toBeReturned).when(T mock).方法 。
    示例:

  • 成功獲取id為1的用戶。
  • /***成功獲取id為1的用戶 */@Testpublic void testGetUserSuccess() {when(findingController.getUser(1)).thenReturn(successResult);String result = findingController.getUser(1);assertEquals(successResult,result);}
  • 使用doReturn成功獲取id為1的用戶
  • /***使用doReturn成功獲取id為1的用戶 */@Testpublic void testGetUserSuccessWith1DoReturn() {doReturn(successResult).when(findingController).getUser(1);String result = findingController.getUser(1);assertEquals(successResult,result);}
  • 成功獲取任意id的用戶
  • /***成功獲取任意id的用戶 */@Testpublic void testGetUserSuccessWithAnyInt() {when(findingController.getUser(anyInt())).thenReturn(successResult);String result = findingController.getUser(1);assertEquals(successResult,result);}
  • 無深度mock對象調用方法
  • /*** userService和user對象都是mock出來的,這里可以通過deepMock省略一個對象*/@Testpublic void testGetUserSuccessWithoutDeep() {when(userService.findById(1)).thenReturn(user);User user = userService.findById(1);when(user.getName()).thenReturn(successResult);String result = user.getName();assertEquals(successResult,result);}
  • 深度mock對象調用方法
    一步到位,無需多次mock
  • /*** deepMock*/@Testpublic void testGetUserSuccessWithDeep() {when(userService.findById(1).getName()).thenReturn(successResult);String userName = userService.findById(1).getName();assertEquals(successResult,userName);}

    拋出異常方法調用

    語法一: when(T methodCall).thenThrow(Class<? extends Throwable>… throwableClasses);這種方法已經被derecated了,基本不用。
    語法二: doThrow(Class<? extends Throwable> toBeThrown).when(T mock).方法;
    示例:

    /***方法一:拋出異常 */@SuppressWarnings("unchecked")@Testpublic void testGetUserException1WithAnyInt() {//這種方法已經被deprecated了,可以用方法二when(findingController.getUser(anyInt())).thenThrow(UnsupportedOperationException.class);assertThrows(UnsupportedOperationException.class, ()->{ findingController.getUser(1);});}/***方法二:拋出異常 */@Testpublic void testGetUserExceptionWithAnyInt() {doThrow(UnsupportedOperationException.class).when(findingController).getUser(anyInt());assertThrows(UnsupportedOperationException.class, ()->{ findingController.getUser(1);});}

    無返回值方法調用

    語法: doNothing().when(T mock).方法; 調用完方法后需要驗證此方法是被調用的次數。verify(T mock, VerificationMode mode).方法。其中mode表示方法被調用的次數,可以不傳,默認為times(1)。
    示例:

    /*** 測試返回值為空的方法*/ @Test public void testClear() {doNothing().when(findingController).clear();findingController.clear();//驗證次方法是否被執行過一次verify(findingController,times(1)).clear();}

    迭代方法調用

    語法一: when(T methodCall).thenReturn(Object value,Object … values);
    語法二: when(T methodCall).thenReturn(Object value,Object … values);
    針對同一個方法,希望每一次的調用結果不同,就應該用到此方法
    示例:

    /*** 測試迭代調用的方法,語法一*/@Testpublic void testGetGroup1() {when(findingController.getGroup()).thenReturn(list);when(list.size()).thenReturn(1,2,3,4);assertEquals(1, list.size());assertEquals(2, list.size());assertEquals(3, list.size());assertEquals(4, list.size());}/*** 測試迭代調用的方法,語法二*/@Testpublic void testGetGroup2() {when(findingController.getGroup()).thenReturn(list);when(list.size()).thenReturn(1).thenReturn(2).thenReturn(3).thenReturn(4);assertEquals(1, list.size());assertEquals(2, list.size());assertEquals(3, list.size());assertEquals(4, list.size());}

    thenAnswer處理業務邏輯

    語法: when(T methodCall).thenAnswer(Answer<?> answer)
    當有一些結果為動態結果時,就要用這種方式了。
    示例:

    /*** 使用doAnswer測試一定的邏輯. 返回下標值的5倍*/@Testpublic void testDoAnswer() {when(findingController.getGroup()).thenReturn(list);when(list.get(anyInt())).thenAnswer(answer->{Integer index = answer.getArgumentAt(0, Integer.class);return String.valueOf(index*5);});assertEquals("0", list.get(0));assertEquals("50", list.get(10));}

    mock對象調用真實方法

    語法: when(T methodCall).thenCallRealMethod();
    使用mock對象調用真實的方法就需要使用到此種方式。注意:這里調用的方法不能是抽象方法
    示例:

    /*** 測試調用真正的方法*/@Testpublic void testGetAge() {when(userService.getAge()).thenCallRealMethod();assertEquals(18, userService.getAge());}

    spy 部分mock

    語法: Object o = spy(真實對象);
    在有些測試場景下,需要測試真實對象的值,對部分只能使用mock來處理,可以借助spy來完成。
    示例:

    /*** 測試部分mock*/public void testSpy() {List<String> userNameList = new ArrayList<String>();List<String> spyList = spy(userNameList);spyList.add("A");spyList.add("B");spyList.add("C");assertEquals("A", spyList.get(0));assertEquals("B", spyList.get(1));assertEquals("C", spyList.get(2));assertEquals(false, spyList.isEmpty());when(spyList.isEmpty()).thenReturn(true);when(spyList.size()).thenReturn(100);assertEquals(true, spyList.isEmpty());assertEquals(100, spyList.size());}

    mock參數匹配

    在前面的示例中提到參數匹配,就是當調用一個方法時,應該返回什么樣的值。這里主要介紹一下以下幾種方式。

    isA()類型判斷

    示例:
    測試getName()中傳入的是否為Base類型的對象

    /*** 類型判斷測試isA*/@Testpublic void testGetNameWithIsA() {when(userService.getName(isA(Base.class))).thenReturn(successResult);String name = userService.getName(new User());assertEquals(successResult, name);}

    any類型判斷

    這個方法我基本沒有用過,感覺意義不大
    示例:

    /*** 類型判斷測試any*/@Testpublic void testGetNameWithAny() {when(userService.getName(any(User.class))).thenReturn(successResult);String name = userService.getName(new Client());assertEquals(successResult, name);}

    通配符的使用

    前文中使用過通配符anyInt(), Mockito中提供了好幾個通配符,這里舉兩個例子。使用通配符的時候一定要注意,參數列表中一處使用通配符,就得處處使用通配符,如果想傳入特定的值一定要使用eq()方法。通配符的優先級大于eq(),如果同樣的方法多個mock,通配符寫在最后一句的話會覆蓋前面mock的結果。
    示例:

    /*** 通配符測試*/@Testpublic void testGetNameByIdAndName() {when(userService.getAgeByIdAndName(anyInt(), anyString())).thenReturn(100);int result1 = userService.getAgeByIdAndName(1, "A");assertEquals(100, result1);// 如果想傳入指定的值,下面這種方法不支持. Mockito 中如果多個參數,一處使用通配符處處使用通配符。/** when(userService.getAgeByIdAndName(anyInt(), "B")).thenReturn(200); int* result2 = userService.getAgeByIdAndName(1, "B"); assertEquals(200, result2);*/// 傳入指定的值必須使用eq()when(userService.getAgeByIdAndName(anyInt(), eq("B"))).thenReturn(200);int result2 = userService.getAgeByIdAndName(1, "B");assertEquals(200, result2);}

    hamcrest 的使用

    hamcrest中提供了很多強大的功能,配合assertThat()使用會增加代碼的可讀性,代碼編寫風格符合陳述式風格。另外,可以不用使用assertTure(), assertEquals()等斷言方法,使用一個assertThat()就可以了。以下示例獨立于文章開頭的業務代碼。但assertThat已經deprecated了,不知道為什么。
    示例:

    @Testpublic void testHamcrest() {int i = 20;double price = 13.2;assertThat(i, equalTo(20));assertThat(i, not(equalTo(210)));assertThat(i, is(20));assertThat(i, is(not(100)));assertThat(price,either(equalTo(13.2)).or(equalTo(10)));//匹配任何一個assertThat(price,anyOf(is(5),is(13.2),not(18)));}

    結束語

    終于寫完了,真心不容易,不過挺開心。Mockito中常用點都在這里了,當然Mockito中還有很多知識點,大家可以參考官網。希望本文能幫助大家,祝大家在IT之路上少走彎路,一路綠燈不堵車,測試一性通過,bug秒解!

    總結

    以上是生活随笔為你收集整理的欧美企业必备技能-Mockito的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。