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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

使用Mockito和BeanPostProcessors在Spring注入测试双打

發布時間:2023/12/3 javascript 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用Mockito和BeanPostProcessors在Spring注入测试双打 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

我非常確定,如果您曾經使用過Spring并且熟悉單元測試,那么您會遇到與您不想修改的Spring應用程序上下文中注入模擬/間諜(測試雙打)有關的問題。 本文介紹了一種使用Spring組件解決此問題的方法。

項目結構


讓我們從項目結構開始:
像往常一樣提出問題,我試圖顯示一個非常簡單的項目結構。 如果我像我們在項目中那樣擴大問題的范圍,我將要展示的方法可能會顯示出更多的好處:

  • 我們有數十個接口和實現自動連接到列表
  • 我們希望基于現有的Spring應用程序上下文執行一些功能測試
  • 我們想要驗證對于某些輸入條件,某些特定的實現將執行其方法
  • 我們想存根數據庫訪問。

在這個例子中,我們有一個PlayerService ,它使用PlayerWebService獲取一個Player 。 我們有一個applicationContext,它僅定義用于自動裝配的包:

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"><context:component-scan base-package="com.blogspot.toomuchcoding"/></beans>

然后我們有一個非常簡單的模型:

播放器

package com.blogspot.toomuchcoding.model;import java.math.BigDecimal;/*** User: mgrzejszczak* Date: 08.08.13* Time: 14:38*/ public final class Player {private final String playerName;private final BigDecimal playerValue;public Player(final String playerName, final BigDecimal playerValue) {this.playerName = playerName;this.playerValue = playerValue;}public String getPlayerName() {return playerName;}public BigDecimal getPlayerValue() {return playerValue;} }

PlayerService的實現,該實現使用PlayerWebService檢索有關Player數據:

PlayerServiceImpl.java

package com.blogspot.toomuchcoding.service;import com.blogspot.toomuchcoding.model.Player; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;/*** User: mgrzejszczak* Date: 08.06.13* Time: 19:02*/ @Service public class PlayerServiceImpl implements PlayerService {private static final Logger LOGGER = LoggerFactory.getLogger(PlayerServiceImpl.class);@Autowiredprivate PlayerWebService playerWebService;@Overridepublic Player getPlayerByName(String playerName) {LOGGER.debug(String.format("Logging the player web service name [%s]", playerWebService.getWebServiceName()));return playerWebService.getPlayerByName(playerName);}public PlayerWebService getPlayerWebService() {return playerWebService;}public void setPlayerWebService(PlayerWebService playerWebService) {this.playerWebService = playerWebService;} }

作為數據提供者的PlayerWebService的實現(在這種情況下,我們正在模擬等待響應的時間):

PlayerWebServiceImpl.java

package com.blogspot.toomuchcoding.service;import com.blogspot.toomuchcoding.model.Player; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service;import java.math.BigDecimal;/*** User: mgrzejszczak* Date: 08.08.13* Time: 14:48*/ @Service public class PlayerWebServiceImpl implements PlayerWebService {private static final Logger LOGGER = LoggerFactory.getLogger(PlayerWebServiceImpl.class);public static final String WEB_SERVICE_NAME = "SuperPlayerWebService";public static final String SAMPLE_PLAYER_VALUE = "1000";@Overridepublic String getWebServiceName() {return WEB_SERVICE_NAME;}@Overridepublic Player getPlayerByName(String name) {try {LOGGER.debug("Simulating awaiting time for a response from a web service");Thread.sleep(5000);} catch (InterruptedException e) {LOGGER.error(String.format("[%s] occurred while trying to make the thread sleep", e));}return new Player(name, new BigDecimal(SAMPLE_PLAYER_VALUE));} }

也許項目的結構和方法不是您見過的最出色的方法之一,但我想讓問題的表達保持簡單;)

問題

那么到底是什么問題呢? 讓我們假設我們希望自動連接的PlayerWebServiceImpl是可以驗證的間諜。 而且,您不想實際更改applicationContext.xml任何內容,而是想要使用Spring上下文的當前版本。

使用模擬程序更容易,因為您可以在XML文件中定義(使用Mockito工廠方法),將bean作為模擬程序來覆蓋原始實現,如下所示:

<bean id="playerWebServiceImpl" class="org.mockito.Mockito" factory-method="mock"><constructor-arg value="com.blogspot.toomuchcoding.service.PlayerWebServiceImpl"/></bean>

那間諜呢? 因為要創建間諜,您需要給定類型的現有對象,因此問題更加嚴重。 在我們的示例中,我們進行了一些自動裝配,因此我們必須首先創建一個PlayerWebService類型的spring bean(Spring必須連接其所有依賴項),然后將其包裝在Mockito.spy(...) ,然后是否必須將其連接到其他地方…變得非常復雜,不是嗎?

解決方案

您可以看到問題并不是那么容易解決的。 解決該問題的一種簡單方法是使用本機Spring機制– BeanPostProcessors。 您可以查看有關如何為指定類型創建Spring BeanPostProcessor的文章-在本示例中將使用它。

讓我們從檢查測試類開始:

PlayerServiceImplTest.java

package com.blogspot.toomuchcoding.service;import com.blogspot.toomuchcoding.model.Player; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import java.math.BigDecimal;import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import static org.mockito.BDDMockito.doReturn; import static org.mockito.Mockito.verify;/*** User: mgrzejszczak* Date: 08.06.13* Time: 19:26*/ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:testApplicationContext.xml") public class PlayerServiceImplTest {public static final String PLAYER_NAME = "Lewandowski";public static final BigDecimal PLAYER_VALUE = new BigDecimal("35000000");@AutowiredPlayerWebService playerWebServiceSpy;@AutowiredPlayerService objectUnderTest;@Testpublic void shouldReturnAPlayerFromPlayerWebService(){//givenPlayer referencePlayer = new Player(PLAYER_NAME, PLAYER_VALUE);doReturn(referencePlayer).when(playerWebServiceSpy).getPlayerByName(PLAYER_NAME);//whenPlayer player = objectUnderTest.getPlayerByName(PLAYER_NAME);//thenassertThat(player, is(referencePlayer));verify(playerWebServiceSpy).getWebServiceName();assertThat(playerWebServiceSpy.getWebServiceName(), is(PlayerWebServiceImpl.WEB_SERVICE_NAME));}}

在此測試中,我們希望模擬從PlayerWebService檢索Player (假設正常情況下它將嘗試向外界發送請求,并且我們不希望這種情況發生),并測試PlayerService返回了我們在方法存根中提供的Player ,以及我們想對Spy進行驗證,以確認方法getWebServiceName()已執行并且其返回值定義得非常精確。 換句話說,我們想對方法getPlayerByName(...)進行存根,并希望通過檢查getWebServiceName()方法來對間諜進行驗證。

讓我們檢查一下測試上下文:

testApplicationContext.xml

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><import resource="applicationContext.xml"/><bean class="com.blogspot.postprocessor.PlayerWebServicePostProcessor" /> </beans>

測試上下文非常小,因為它會導入當前的applicationContext.xml并創建一個Bean,這是此示例中的關鍵功能– BeanPostProcessor :

PlayerWebServicePostProcessor.java

package com.blogspot.postprocessor;import com.blogspot.toomuchcoding.processor.AbstractBeanPostProcessor; import com.blogspot.toomuchcoding.service.PlayerWebService;import static org.mockito.Mockito.spy;/*** User: mgrzejszczak* Date: 07.05.13* Time: 11:30*/ public class PlayerWebServicePostProcessor extends AbstractBeanPostProcessor<PlayerWebService> {public PlayerWebServicePostProcessor() {super(PlayerWebService.class);}@Overridepublic PlayerWebService doBefore(PlayerWebService bean) {return spy(bean);}@Overridepublic PlayerWebService doAfter(PlayerWebService bean) {return bean;} }

該類擴展了實現BeanPostProcessor接口的AbstractBeanPostProcessor 。 這個類背后的邏輯是注冊類為其中一個想要之前任一初始化(執行某些動作postProcessBeforeInitialization )或豆(初始化之后postProcessAfterInitialization )。 我的帖子中很好地解釋了AbstractBeanPostProcessor
Spring BeanPostProcessor用于指定的類型,但是有一點點變化–在我的舊文章中,抽象允許我們對Bean執行一些操作,而不能在Bean上返回包裝器或代理。

如您在初始化之前使用Mockito.spy(...) PlayerWebServicePostProcessor ,我們正在使用Mockito.spy(...)方法創建一個Spy。 通過這種方式,我們在給定類型的Bean的初始化上創建了一個工廠鉤子-就這么簡單。 對于實現PlayerWebService接口的所有類,將執行此方法。

其他可能性

在檢查該問題的當前解決方案時,我遇到了Jakub Janczak的Springockito庫 。

我還沒有使用過它,所以我不知道與此庫相關的生產問題(如果有的話;)),但看起來真的很直觀,很好– Jakub! 盡管如此,您仍然依賴于外部庫,而在此示例中,我展示了如何使用Spring處理問題。

摘要

在這篇文章中,我展示了如何

  • 使用XML Spring配置為現有bean創建模擬
  • 創建一個BeanPostProcessor實現,該實現對給定類的bean執行邏輯
  • 對于給定的bean類,返回Spy(您也可以返回Mock)

現在,讓我們看一下我的方法的優點和缺點:

優點

  • 您使用Spring本機機制為您的bean創建測試雙打
  • 您不需要添加任何其他外部依賴項
  • 如果您使用AbstractBeanPostProcessor ,則只需執行很少的更改

缺點

  • 您必須熟悉內部Spring體系結構(它使用BeanPostProcessors)–但這是不利嗎? ;)–實際上,如果您使用AbstractBeanPostProcessor ,則不必熟悉它–您只需提供類類型和初始化前后要發生的操作即可。
  • 它不像Springockito庫中的注釋那樣直觀

資料來源

源代碼可從TooMuchCoding BitBucket存儲庫和TooMuchCoding Github存儲庫中獲得 。

參考:在博客上 使用我們的JCG合作伙伴 Marcin Grzejszczak的Mockito和BeanPostProcessors在Spring注入測試雙打, 用于編碼成癮者博客。

翻譯自: https://www.javacodegeeks.com/2013/08/injecting-test-doubles-in-spring-using-mockito-and-beanpostprocessors.html

總結

以上是生活随笔為你收集整理的使用Mockito和BeanPostProcessors在Spring注入测试双打的全部內容,希望文章能夠幫你解決所遇到的問題。

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