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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

单元测试中使用Mockito模拟对象

發(fā)布時(shí)間:2023/12/13 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 单元测试中使用Mockito模拟对象 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

單元測(cè)試應(yīng)該小巧玲瓏,輕盈快捷。然而,一個(gè)待測(cè)的對(duì)象可能依賴另一個(gè)對(duì)象。它可能需要跟數(shù)據(jù)庫、郵箱服務(wù)器、Web Service、消息隊(duì)列等服務(wù)進(jìn)行交互。但是,這些服務(wù)可能在測(cè)試過程中不可用。假設(shè)這些服務(wù)可用,依賴這些服務(wù)的單元測(cè)試可能相當(dāng)耗時(shí)。要是

  • Web Service 不可獲得。
  • 數(shù)據(jù)庫因維護(hù)而關(guān)閉。
  • 消息隊(duì)列笨重且緩慢。
  • 這些違背單元測(cè)試小巧玲瓏,輕盈快捷的初衷。單元測(cè)試被期待在幾毫秒內(nèi)執(zhí)行完成。若單元測(cè)試緩慢,你的開發(fā)過程受阻,這會(huì)影響你開發(fā)組的效率。解決之道就是模擬(Mocking),

    若你遵循OOP的SOILD原則,且使用Spring的依賴注入,單元測(cè)試中的模擬Mock變得輕而易舉。你不必連接數(shù)據(jù)庫。你只需一個(gè)能返回你期待值的對(duì)象。若你編寫緊密耦合代碼,模擬會(huì)相當(dāng)艱難。我目睹過許多因緊密耦合其它對(duì)象的遺留代碼不能單元測(cè)試。不可測(cè)試代碼不遵循OOP的SOILD原則,且不能使用依賴注入。

    Mockito初體驗(yàn)

    接下來將學(xué)習(xí)使用Mockito框架。它是一套通過簡(jiǎn)單的方法對(duì)于指定接口或類生產(chǎn)Mock對(duì)象的類庫。使用Mockito,在準(zhǔn)備階段只需少量時(shí)間,可以使用簡(jiǎn)潔的API編寫漂亮的測(cè)試,可以對(duì)具體類創(chuàng)建Mock對(duì)象,并且有監(jiān)視非Mock對(duì)象的功能。

    這有兩個(gè)術(shù)語需要了解一下。

    • Stub對(duì)象作用是在測(cè)試時(shí)提供所需的測(cè)試數(shù)據(jù),可以對(duì)各種交互設(shè)置相應(yīng)的回應(yīng)。Mockito中when(...).thenReturn(...)這樣的語法便是設(shè)置方法調(diào)用的返回值。另外也可以設(shè)置方法在何時(shí)調(diào)用會(huì)拋異常等。

    • Mock對(duì)象用來驗(yàn)證測(cè)試中所依賴對(duì)象間的交互是否能夠達(dá)到預(yù)期。Mockito中用verify(...).methodXxx(...)語法驗(yàn)證methodXxx()方法是否按照預(yù)期進(jìn)行調(diào)用。

    需要加入到pom.xml的依賴如下:

    <dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>2.16.0</version><scope>test</scope> </dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope> </dependency>

    創(chuàng)建Mock對(duì)象

    可以通過兩種方法來Mock對(duì)象

  • 通過mock(Class<T> clazz)方法。
  • 通過@Mock注解需要Mock的對(duì)象,然后調(diào)用MockitoAnnotations.initMocks(this)或@RunWith(MockitoJUnitRunner.class)初始化模擬。
  • //@RunWith(MockitoJUnitRunner.class) public class MockitoSampleTest {// 模擬接口UserService mockUserService = mock(UserService.class);// 模擬實(shí)現(xiàn)類UserServiceImpl mockServiceImpl = mock(UserServiceImpl.class);// 基于注釋模擬類@MockUser mockUser;@Beforepublic void initMocks() {// 初始化當(dāng)前測(cè)試類所有@Mock注釋模擬對(duì)象MockitoAnnotations.initMocks(this);} }

    值得注意的是,對(duì)于final類、匿名類和Java的基本類型是無法進(jìn)行Mock的。

    設(shè)定Mock對(duì)象的期望值行為及返回值

    有兩種通用基礎(chǔ)設(shè)定寫法:

  • when(...).thenReturn(...);
  • doReturn(...).when(...).someMethod();
  • 但是,doReturn(...).when(mockObj.someMethod());會(huì)拋異常。

    @Test public void testMockClass() {// 對(duì)方法設(shè)定返回值,也就是設(shè)置數(shù)據(jù)樁when(mockServiceImpl.findUserByUserName("tom")).thenReturn(new User("tom", "1234"));doReturn(true).when(mockServiceImpl).hasMatchUser("tom", "1234");User user = mockServiceImpl.findUserByUserName("tom");boolean isMatch = mockServiceImpl.hasMatchUser("tom", "1234");assertNotNull(user);assertEquals(user.getUserName(), "tom");assertEquals(isMatch, true);}

    也值得注意的是,static和final修飾的方法無法進(jìn)行設(shè)定的。

    驗(yàn)證交互行為

    Mock對(duì)象一旦建立便會(huì)自動(dòng)記錄自己的交互行為,所以,可以有選擇地對(duì)其交互行為進(jìn)行驗(yàn)證。

    @Test // 模擬接口UserService測(cè)試 public void testMockInterface() {// 對(duì)方法設(shè)定返回值,也就是設(shè)置數(shù)據(jù)樁when(mockUserService.findUserByUserName("tom")).thenReturn(new User("tom", "1234"));doReturn(true).when(mockUserService).hasMatchUser("tom", "1234");// 對(duì)void方法進(jìn)行方法預(yù)期設(shè)定User u = new User("John", "1234");doNothing().when(mockUserService).registerUser(u);// 執(zhí)行方法調(diào)用User user = mockUserService.findUserByUserName("tom");boolean isMatch = mockUserService.hasMatchUser("tom", "1234");mockUserService.registerUser(u);assertNotNull(user);assertEquals(user.getUserName(), "tom");assertEquals(isMatch, true);// 驗(yàn)證交互行為verify(mockUserService).findUserByUserName("tom");// 驗(yàn)證方法只調(diào)用一次verify(mockUserService, times(1)).findUserByUserName("tom");// 驗(yàn)證方法至少調(diào)用一次verify(mockUserService, atLeastOnce()).findUserByUserName("tom");verify(mockUserService, atLeast(1)).findUserByUserName("tom");// 驗(yàn)證方法至多調(diào)用一次verify(mockUserService, atMost(1)).findUserByUserName("tom");verify(mockUserService).hasMatchUser("tom", "1234");verify(mockUserService).registerUser(u); }

    對(duì)Service層進(jìn)行單元測(cè)試

    同常主要Java Web應(yīng)用分Controller,Service,DAO基本三層來進(jìn)行開發(fā)。

    接下來通過使用Mockito框架對(duì)Service進(jìn)單元測(cè)試。

    Domain領(lǐng)域?qū)ο?#xff1a;

    public class Product {}

    DAO數(shù)據(jù)連接層:

    public interface ProductDao {int getAvailableProducts(Product product);int orderProduct(Product product, int orderedQuantity); }

    Service業(yè)務(wù)邏輯層:

    public class ProductService {private ProductDao productDao;public boolean buy(Product product, int orderedQuantity) {int availableQuantity = productDao.getAvailableProducts(product);if (orderedQuantity > availableQuantity) {return false;}productDao.orderProduct(product, orderedQuantity);return true;}}

    Service測(cè)試用例:

    public class ProductServiceTest {private ProductDao productDao;@Beforepublic void setupMock() {//模擬Dao層productDao = mock(ProductDao.class);}@Testpublic void testBuy() {int availableQuantity = 30;Product product = new Product();ProductService productService = new ProductService();//設(shè)置數(shù)據(jù)樁when(productDao.getAvailableProducts(product)).thenReturn(availableQuantity);//doReturn(availableQuantity).when(productDao).getAvailableProducts(product);//這寫法不行//doReturn(availableQuantity).when(productDao.getAvailableProducts(product));//通過Spring測(cè)試框架提供的工具類為目標(biāo)對(duì)象私有屬性設(shè)值,這樣就不用ProductDao另建setProductDao()方法ReflectionTestUtils.setField(productService, "productDao", productDao);Assert.assertFalse(productService.buy(product, 31));Assert.assertTrue(productService.buy(product, 3));//驗(yàn)證交互行為verify(productDao).orderProduct(product, 3);verify(productDao, times(2)).getAvailableProducts(product);}}

    測(cè)試用例中,用到Spring test框架的ReflectionTestUtils,它可以為目標(biāo)對(duì)象非公有屬性設(shè)值,或調(diào)用非公有setter方法,方便測(cè)試過程中使用。

    為了使用ReflectionTestUtils,需要向pom.xml添加下面的依賴

    <dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>3.0.5.RELEASE</version><scope>test</scope> </dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>3.0.5.RELEASE</version><scope>test</scope> </dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version><scope>test</scope> </dependency>

    總結(jié)

    本文介紹了Mockito的基本用法,以及通過它Mock對(duì)象對(duì)Service層輔助測(cè)試用例。在Mockito輔助下,單元測(cè)試變得如虎添翼啊!

    在編寫代碼過程中,必須反復(fù)調(diào)試它,保證他順利通過。雖代碼通過編譯,只是說明其語法正確,但不能保證其語義也正確。沒有任何人可以輕易承諾這段代碼的行為一定是正確的。幸運(yùn)的是,單元測(cè)試會(huì)為我們的承諾作出保證。編寫單元測(cè)試就是用來驗(yàn)證這段代碼的行為是否與我們期望的一樣。有了單元測(cè)試,我們可以自信地交付自己的代碼,減少后顧之憂。

    引用

  • Mocking in Unit Tests with Mockito

  • Mockito (Mockito 2.16.0 API)

  • 《Spring 3.x企業(yè)應(yīng)用開發(fā)實(shí)戰(zhàn)》陳雄華、林開雄 著

  • Spring Framework Reference Documentation 11. Testing

  • 總結(jié)

    以上是生活随笔為你收集整理的单元测试中使用Mockito模拟对象的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。