欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

基于Springboot+Junit+Mockito做單元測(cè)試的示例

 更新時(shí)間:2018年02月10日 14:28:19   作者:鼯鼠的技術(shù)博客  
本篇文章主要介紹了基于Springboot+Junit+Mockito做單元測(cè)試的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

前言

這篇文章介紹如何使用Springboot+Junit+Mockito做單元測(cè)試,案例選取撮合交易的一個(gè)類(lèi)來(lái)做單元測(cè)試。

單元測(cè)試前先理解需求

要寫(xiě)出好的單測(cè),必須先理解了需求,只有知道做什么才能知道怎么測(cè)。但本文主要講mockito的用法,無(wú)需關(guān)注具體需求。所以本節(jié)略去具體的需求描述。

隔離外部依賴(lài)

Case1. 被測(cè)類(lèi)中被@Autowired 或 @Resource 注解標(biāo)注的依賴(lài)對(duì)象,如何控制其返回值

以被測(cè)方法 MatchingServiceImpl.java的matching(MatchingOrder buyOrder, MatchingOrder sellOrder)為例

被測(cè)類(lèi)MatchingServiceImpl

public class MatchingServiceImpl implements MatchingService {
  private static final Logger log = LoggerFactory.getLogger(MatchingServiceImpl.class);
  @Autowired
  private QuoteService quoteService;
  ...
  public MatchingResult matching(MatchingOrder buyOrder, MatchingOrder sellOrder) {
    int currentPrice = quoteService.getCurrentPriceByProduct(buyOrder.getProductCode());
    MatchingResult result = new MatchingResult();
    if (sellOrder != null && buyOrder != null &&
        sellOrder.getPrice() <= buyOrder.getPrice()) {
    ...
  }
}

matching方法中的quoteService.getCurrentPriceByProduct(buyOrder.getProductCode());要訪問(wèn)Redis獲取當(dāng)前報(bào)價(jià),這里我們需要把外部依賴(lài)quoteService mock掉,控制getCurrentPriceByProduct方法的返回值。使用mockito可以做到,具體如下:

測(cè)試類(lèi)MatchingServiceImplTest

public class MatchingServiceImplTest extends MockitoBasedTest {
  /**
   * 被@Mock標(biāo)注的對(duì)象會(huì)自動(dòng)注入到被@InjectMocks標(biāo)注的對(duì)象中
   */
  @Mock
  private QuoteService quoteService;
  /**
   * <pre>
   * 被測(cè)對(duì)象,用@InjectMocks標(biāo)注,那些被@mock標(biāo)注的對(duì)象就會(huì)自動(dòng)注入其中。
   * 另一個(gè)注意點(diǎn)是這里的MatchingServiceImpl是直接new出來(lái)(Mockito 1.9版本后不new也可以),而不是通過(guò)spring容器注入的。因?yàn)檫@里我不需要從spring容器中
   * 獲得其他依賴(lài),不需要database ,redis ,zookeeper,mq,啥都不依賴(lài),所以直接new
   * </pre>
   */
  @InjectMocks
  private MatchingServiceImpl matchingService = new MatchingServiceImpl();
  @Test
  public void testMatching_SuccessWhenCurrentPriceBetweenBuyPriceAndSellPrice() {
    MatchingOrder buyOrder = new MatchingOrder();
    buyOrder.setPrice(1000);
    buyOrder.setCount(23);
    MatchingOrder sellOrder = new MatchingOrder();
    sellOrder.setPrice(800);
    sellOrder.setCount(20);
    // 方法打樁(Method stubbing)
    // when(x).thenReturn(y) :當(dāng)指定方法被調(diào)用時(shí)返回指定值
    Mockito.when(quoteService.getCurrentPriceByProduct(Mockito.anyString())).thenReturn(900);
    MatchingResult result = matchingService.matching(buyOrder, sellOrder);
    org.junit.Assert.assertEquals(true, result.isSuccess());// 斷言撮合是否成功
    org.junit.Assert.assertEquals(20, result.getTradeCount());// 斷言成交數(shù)量
    org.junit.Assert.assertEquals(900, result.getTradePrice()); // 斷言最新報(bào)價(jià)是否符合預(yù)期
  }

Case2. 被測(cè)函數(shù)A調(diào)用被測(cè)類(lèi)其他函數(shù)B,怎么控制函數(shù)B的返回值?

比如,MatchingServiceImpl中有個(gè)函數(shù)startBuyProcess,它里面調(diào)用了該類(lèi)中的其他函數(shù),如getTopSellOrder,matching,如何控制這兩個(gè)函數(shù)的返回值?
這里要解決的問(wèn)題其實(shí)是怎么對(duì)一個(gè)類(lèi)”部分mock”–被測(cè)類(lèi)的被測(cè)方法(如startBuyProcess)要真實(shí)執(zhí)行,而另一些方法(如getTopSellOrder)則是要打樁(不真正進(jìn)去執(zhí)行)。

被測(cè)類(lèi)MatchingServiceImpl

protected void startBuyProcess(MatchingOrder buyOrder, boolean waitForMatching) {
    while (true) {
      //對(duì)手方最優(yōu)價(jià)
      MatchingOrder topSellOrder = getTopSellOrder(buyOrder.getProductCode());
      MatchingResult matchingResult = matching(buyOrder,topSellOrder);
      if(matchingResult.isSuccess()) {
        doMatchingSuccess(buyOrder,topSellOrder,matchingResult,MatchingType.BUY);
        if(buyOrder.getCount() <= 0) {
          break;
        }
      }else {
        if(waitForMatching) {
          //加入待撮合隊(duì)列
          addToMatchingBuy(buyOrder);
        }else {
          //撤單
          sendCancleMsg(buyOrder);
        }
        break;
      }
    }
  }

利用Mockito.spy()可以做到“部分Mock”

測(cè)試類(lèi)MatchingServiceImplTest.testStartBuyProcess_InCaseOfMatchingSuccess

/**
   *
   * 測(cè)試StartBuyProcess方法在撮合成功后的處理是否符合預(yù)期,即測(cè)試startBuyProcess方法進(jìn)入下面這個(gè)判斷分支后的行為
   * {@link MatchingServiceImpl#startBuyProcess(MatchingOrder, boolean)}
   *
   * <pre>
   * if (matchingResult.isSuccess()) {
   *
   *   doMatchingSuccess(buyOrder, topSellOrder, matchingResult, MatchingType.BUY);
   *
   *   if (buyOrder.getCount() &lt;= 0) {
   *     break;
   *   }
   * }
   * </pre>
   *
   */
  @Test
  public void testStartBuyProcess_InCaseOfMatchingSuccess() {
    MatchingOrder buyOrder = new MatchingOrder();
    buyOrder.setPrice(700);
    buyOrder.setCount(23);
    // 用Mockito.spy()對(duì)matchingService進(jìn)行部分打樁
    matchingService = Mockito.spy(matchingService);
    MatchingResult firstMatchingResult = new MatchingResult();
    firstMatchingResult.setSuccess(true);
    firstMatchingResult.setTradeCount(20);
    MatchingResult secondMatchingResult = new MatchingResult();
    secondMatchingResult.setSuccess(false);
    // doReturn(x).when(obj).method() 對(duì)方法打樁,打樁后,程序執(zhí)行這些方法時(shí)將按照預(yù)期返回指定值,未被打樁的方法將真實(shí)執(zhí)行
    // 兩個(gè)doReturn表示第一次調(diào)用matchingService.matching時(shí)返回firstMatchingResult,第二次調(diào)用返回secondMatchingResult
    // 因?yàn)閟tartBuyProcess里有個(gè)while循壞,可能會(huì)多次執(zhí)行matching方法
    Mockito.doReturn(firstMatchingResult).doReturn(secondMatchingResult).when(matchingService)
        .matching(Mockito.any(MatchingOrder.class), Mockito.any(MatchingOrder.class));
    MatchingOrder sellOrder = new MatchingOrder();
    sellOrder.setPrice(600);
    sellOrder.setCount(20);
    // 對(duì)getTopSellOrder方法打樁
    Mockito.doReturn(sellOrder).when(matchingService).getTopSellOrder(Mockito.anyString());
    // 對(duì)外部依賴(lài)jedis的方法進(jìn)行打樁
    Mockito.when(jedisClient.incrBy(Mockito.anyString(), Mockito.anyLong())).thenReturn(0L);
    // startBuyProcess是被測(cè)函數(shù),不打樁,會(huì)真實(shí)執(zhí)行
    matchingService.startBuyProcess(buyOrder, true);
    // 后面的校驗(yàn)和斷言是測(cè)試doMatchingSuccess方法的行為的,這也是這個(gè)測(cè)試的目的
    // verify可用來(lái)校驗(yàn),某個(gè)類(lèi)的方法被執(zhí)行過(guò)多少次,這里是校驗(yàn)jedisClient.zremFirst是否被執(zhí)行過(guò)1次
    Mockito.verify(jedisClient, Mockito.times(1)).zremFirst(Mockito.anyString());
    org.junit.Assert.assertEquals(3, buyOrder.getCount());
    org.junit.Assert.assertEquals(0, sellOrder.getCount());
  }

spy的用法已經(jīng)演示完畢,下面從testStartBuyProcess_InCaseOfMatchingSuccess說(shuō)下單元測(cè)試的“粒度”。

testStartBuyProcess_InCaseOfMatchingSuccess的目的是想測(cè)doMatchingSuccess,我們費(fèi)了很大勁才把前面的一堆準(zhǔn)備工作做完,才能去測(cè)doMatchingSuccess。

更好的實(shí)踐應(yīng)該是另起測(cè)試方法去單獨(dú)測(cè)doMatchingSuccess,關(guān)注點(diǎn)也集中很多,doMatchingSuccess覆蓋完了,再測(cè)startBuyProcess其實(shí)就只是覆蓋下它本身的判斷分支就行了。覆蓋率照樣達(dá)到,而且測(cè)試代碼也更容易維護(hù),testStartBuyProcess_InCaseOfMatchingSuccess由于考慮的職責(zé)太多,它很容易受到變化的影響,細(xì)小的東西改變,可能就會(huì)影響它的正常工作。

引入測(cè)試框架Maven依賴(lài)

<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.11</version>
  <scope>test</scope>
</dependency>

<dependency>
  <groupId>org.mockito</groupId>
  <artifactId>mockito-all</artifactId>
  <version>1.10.19</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-test</artifactId>
  <version>4.2.5.RELEASE</version>
  <scope>test</scope>
</dependency>

springboot+junit+mockito的上下文構(gòu)建

MockitoBasedTest

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestApplication.class)
public abstract class MockitoBasedTest {
  @Before
  public void setUp() throws Exception {
    // 初始化測(cè)試用例類(lèi)中由Mockito的注解標(biāo)注的所有模擬對(duì)象
    MockitoAnnotations.initMocks(this);
  }
}
// 其他測(cè)試類(lèi)繼承MockitoBasedTest

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java中的重要核心知識(shí)點(diǎn)之繼承詳解

    Java中的重要核心知識(shí)點(diǎn)之繼承詳解

    繼承是java面向?qū)ο缶幊碳夹g(shù)的一塊基石,因?yàn)樗试S創(chuàng)建分等級(jí)層次的類(lèi)。繼承就是子類(lèi)繼承父類(lèi)的特征和行為,使得子類(lèi)對(duì)象(實(shí)例)具有父類(lèi)的實(shí)例域和方法,或子類(lèi)從父類(lèi)繼承方法,使得子類(lèi)具有父類(lèi)相同的行為
    2021-10-10
  • 二維碼生成Java實(shí)現(xiàn)代碼

    二維碼生成Java實(shí)現(xiàn)代碼

    這篇文章主要為大家詳細(xì)介紹了二維碼生成Java實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • Java 函數(shù)編程詳細(xì)介紹

    Java 函數(shù)編程詳細(xì)介紹

    這篇文章主要介紹了Java函數(shù)式編程,lambda表達(dá)式可以被認(rèn)為是一個(gè)匿名函數(shù),可以在函數(shù)接口的上下文中使用。函數(shù)接口是只指定一個(gè)抽象方法的接口,下面來(lái)看文章的詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-11-11
  • Spring動(dòng)態(tài)添加定時(shí)任務(wù)的實(shí)現(xiàn)思路

    Spring動(dòng)態(tài)添加定時(shí)任務(wù)的實(shí)現(xiàn)思路

    這篇文章主要介紹了Spring動(dòng)態(tài)添加定時(shí)任務(wù)的實(shí)現(xiàn)思路,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-07-07
  • SpringBoot2如何集成Elasticsearch6.x(TransportClient方式)

    SpringBoot2如何集成Elasticsearch6.x(TransportClient方式)

    這篇文章主要介紹了SpringBoot2如何集成Elasticsearch6.x(TransportClient方式)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • 利用javadoc注釋自動(dòng)生成Swagger注解

    利用javadoc注釋自動(dòng)生成Swagger注解

    由于現(xiàn)在controller方法上面沒(méi)有swagger注解,只能拿到接口url地址,無(wú)法獲得接口功能描述,所以本文為大家介紹一下如何利用javadoc注釋自動(dòng)生成Swagger注解,感興趣的可以了解下
    2023-08-08
  • 解決Maven打包只有幾十K,運(yùn)行報(bào)錯(cuò)no main manifest attribute問(wèn)題

    解決Maven打包只有幾十K,運(yùn)行報(bào)錯(cuò)no main manifest attribute

    這篇文章主要介紹了解決Maven打包只有幾十K,運(yùn)行報(bào)錯(cuò)no main manifest attribute問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • WIN10系統(tǒng)中添加bat腳本重啟jar服務(wù)

    WIN10系統(tǒng)中添加bat腳本重啟jar服務(wù)

    在bat腳本中執(zhí)行java服務(wù),命令與cmd中類(lèi)似,下面這篇文章主要給大家介紹了關(guān)于WIN10系統(tǒng)中添加bat腳本重啟jar服務(wù)的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-12-12
  • 一篇文章教你將JAVA的RabbitMQz與SpringBoot整合

    一篇文章教你將JAVA的RabbitMQz與SpringBoot整合

    這篇文章主要介紹了如何將JAVA的RabbitMQz與SpringBoot整合,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2021-09-09
  • 定時(shí)任務(wù)注解@Scheduled不生效問(wèn)題及解決

    定時(shí)任務(wù)注解@Scheduled不生效問(wèn)題及解決

    這篇文章主要介紹了定時(shí)任務(wù)注解@Scheduled不生效問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06

最新評(píng)論