绵阳网站推广排名,常见的建站工具,上海58同城官网,网站建设黄页在线免费我非常确定#xff0c;如果您曾经使用过Spring并且熟悉单元测试#xff0c;那么您会遇到与您不想修改的Spring应用程序上下文中注入模拟/间谍#xff08;测试双打#xff09;有关的问题。 本文介绍了一种使用Spring组件解决此问题的方法。 项目结构 让我们从项目结构开始如果您曾经使用过Spring并且熟悉单元测试那么您会遇到与您不想修改的Spring应用程序上下文中注入模拟/间谍测试双打有关的问题。 本文介绍了一种使用Spring组件解决此问题的方法。 项目结构 让我们从项目结构开始 像往常一样提出问题我试图显示一个非常简单的项目结构。 如果我像我们在项目中那样扩大问题的范围我将要展示的方法可能会显示出更多的好处 我们有数十个接口和实现自动连接到列表 我们希望基于现有的Spring应用程序上下文执行一些功能测试 我们想要验证对于某些输入条件某些特定的实现将执行其方法 我们想存根数据库访问。 在这个例子中我们有一个PlayerService 它使用PlayerWebService获取一个Player 。 我们有一个applicationContext它仅定义用于自动装配的包 applicationContext.xml ?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxsi:schemaLocationhttp://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.xsdcontext:component-scan base-packagecom.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 idplayerWebServiceImpl classorg.mockito.Mockito factory-methodmockconstructor-arg valuecom.blogspot.toomuchcoding.service.PlayerWebServiceImpl//bean 那间谍呢 因为要创建间谍您需要给定类型的现有对象因此问题更加严重。 在我们的示例中我们进行了一些自动装配因此我们必须首先创建一个PlayerWebService类型的spring beanSpring必须连接其所有依赖项然后将其包装在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 version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdimport resourceapplicationContext.xml/bean classcom.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 AbstractBeanPostProcessorPlayerWebService {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