建设通是不是官方网站,教人做窗帘的视频网站,网页设计可以自学吗,松江大学城网站建设mockito 静态方法在代码中#xff0c;我最近遇到了一段非常糟糕的代码#xff0c;该代码基于对对象执行某些操作的类转换。 当然#xff0c;代码需要重构#xff0c;但是如果您首先没有对该功能进行单元测试#xff0c;则有时您可能无法做到/或者不想这样做#xff08;这… mockito 静态方法 在代码中我最近遇到了一段非常糟糕的代码该代码基于对对象执行某些操作的类转换。 当然代码需要重构但是如果您首先没有对该功能进行单元测试则有时您可能无法做到/或者不想这样做这应该是可以理解的。 在下面的文章中我将展示如何测试这种代码如何重构它以及实际上我对这种代码的看法。 让我们看一下项目结构 如关于Mocktio RETURNS_DEEP_STUBS for JAXB的帖子中所述我们再次在com.blogspot.toomuchcoding.model包中使用了JAXB编译器生成的JAXB类。 让我们省略对pom.xml文件的讨论因为它与上一篇文章完全相同。 在com.blogspot.toomuchcoding.adapter包中我们在JAXB PlayerDetails类上具有适配器该类提供对Player接口的访问。 有 CommonPlayerAdapter.java package com.blogspot.toomuchcoding.adapter;import com.blogspot.toomuchcoding.model.Player;
import com.blogspot.toomuchcoding.model.PlayerDetails;/*** User: mgrzejszczak* Date: 09.06.13* Time: 15:42*/
public class CommonPlayerAdapter implements Player {private final PlayerDetails playerDetails;public CommonPlayerAdapter(PlayerDetails playerDetails){this.playerDetails playerDetails;}Overridepublic void run() {System.out.printf(Run %s. Run!%n, playerDetails.getName());}public PlayerDetails getPlayerDetails() {return playerDetails;}
}DefencePlayerAdapter.java package com.blogspot.toomuchcoding.adapter;import com.blogspot.toomuchcoding.model.DJ;
import com.blogspot.toomuchcoding.model.DefensivePlayer;
import com.blogspot.toomuchcoding.model.JavaDeveloper;
import com.blogspot.toomuchcoding.model.PlayerDetails;/*** User: mgrzejszczak* Date: 09.06.13* Time: 15:42*/
public class DefencePlayerAdapter extends CommonPlayerAdapter implements DefensivePlayer, DJ, JavaDeveloper {public DefencePlayerAdapter(PlayerDetails playerDetails){super(playerDetails);}Overridepublic void defend(){System.out.printf(Defence! %s. Defence!%n, getPlayerDetails().getName());}Overridepublic void playSomeMusic() {System.out.println(Oops I did it again...!);}Overridepublic void doSomeSeriousCoding() {System.out.println(System.out.println(\Hello world\););}
}OffensivePlayerAdapter.java package com.blogspot.toomuchcoding.adapter;import com.blogspot.toomuchcoding.model.OffensivePlayer;
import com.blogspot.toomuchcoding.model.PlayerDetails;/*** User: mgrzejszczak* Date: 09.06.13* Time: 15:42*/
public class OffensivePlayerAdapter extends CommonPlayerAdapter implements OffensivePlayer {public OffensivePlayerAdapter(PlayerDetails playerDetails){super(playerDetails);}Overridepublic void shoot(){System.out.printf(%s Shooooot!.%n, getPlayerDetails().getName());}
} 好的现在让我们转到更有趣的部分。 让我们假设我们有一个非常简单的玩家工厂 PlayerFactoryImpl.java package com.blogspot.toomuchcoding.factory;import com.blogspot.toomuchcoding.adapter.CommonPlayerAdapter;
import com.blogspot.toomuchcoding.adapter.DefencePlayerAdapter;
import com.blogspot.toomuchcoding.adapter.OffensivePlayerAdapter;
import com.blogspot.toomuchcoding.model.Player;
import com.blogspot.toomuchcoding.model.PlayerDetails;
import com.blogspot.toomuchcoding.model.PositionType;/*** User: mgrzejszczak* Date: 09.06.13* Time: 15:53*/public class PlayerFactoryImpl implements PlayerFactory {Overridepublic Player createPlayer(PositionType positionType) {PlayerDetails player createCommonPlayer(positionType);switch (positionType){case ATT:return new OffensivePlayerAdapter(player);case MID:return new OffensivePlayerAdapter(player);case DEF:return new DefencePlayerAdapter(player);case GK:return new DefencePlayerAdapter(player);default:return new CommonPlayerAdapter(player);}}private PlayerDetails createCommonPlayer(PositionType positionType){PlayerDetails playerDetails new PlayerDetails();playerDetails.setPosition(positionType);return playerDetails;}
} 好的所以我们有制造Player的工厂。 让我们看一下使用工厂的服务 PlayerServiceImpl.java package com.blogspot.toomuchcoding.service;import com.blogspot.toomuchcoding.factory.PlayerFactory;
import com.blogspot.toomuchcoding.model.*;/*** User: mgrzejszczak* Date: 08.06.13* Time: 19:02*/
public class PlayerServiceImpl implements PlayerService {private PlayerFactory playerFactory;Overridepublic Player playAGameWithAPlayerOfPosition(PositionType positionType) {Player player playerFactory.createPlayer(positionType);player.run();performAdditionalActions(player);return player;}private void performAdditionalActions(Player player) {if(player instanceof OffensivePlayer){OffensivePlayer offensivePlayer (OffensivePlayer) player;performAdditionalActionsForTheOffensivePlayer(offensivePlayer);}else if(player instanceof DefensivePlayer){DefensivePlayer defensivePlayer (DefensivePlayer) player;performAdditionalActionsForTheDefensivePlayer(defensivePlayer);}}private void performAdditionalActionsForTheOffensivePlayer(OffensivePlayer offensivePlayer){offensivePlayer.shoot();}private void performAdditionalActionsForTheDefensivePlayer(DefensivePlayer defensivePlayer){defensivePlayer.defend();try{DJ dj (DJ)defensivePlayer;dj.playSomeMusic();JavaDeveloper javaDeveloper (JavaDeveloper)defensivePlayer;javaDeveloper.doSomeSeriousCoding();}catch(ClassCastException exception){System.err.println(Sorry, I cant do more than just play football...);}}public PlayerFactory getPlayerFactory() {return playerFactory;}public void setPlayerFactory(PlayerFactory playerFactory) {this.playerFactory playerFactory;}
} 让我们承认吧……这段代码很糟糕。 在内部当您查看它时不管它是否使用了 operator 实例 您都会感觉到它是邪恶的。 正如您在代码中看到的那样我们正在进行一些类强制转换……我们到底如何进行测试 在大多数测试框架中您无法对模拟进行此类类转换因为它们是使用CGLIB库构建的并且可能会抛出一些ClassCastExceptions。 您仍然无法返回模拟和真实的实现假设它们在构造过程中不会执行任何丑陋的工作并且它实际上可以工作但仍然如此–这是错误的代码 Mockito可以使用其extraInterfaces功能尽管您不应该过度使用此功能-实际上如果您需要使用它请考虑对其进行重构 extraInterfaces MockSettings extraInterfaces java.lang.Class …接口 指定模拟应实现的额外接口。 对于遗留代码或某些极端情况可能很有用。 有关背景信息请参见此处的问题51。此神秘功能应偶尔使用。 被测对象应该确切知道其协作者和依赖项。 如果您碰巧经常使用它请确保您确实在生成简单干净且可读的代码。 例子 Foo foo mock(Foo.class, withSettings().extraInterfaces(Bar.class, Baz.class));//now, the mock implements extra interfaces, so following casting is possible:Bar bar (Bar) foo;Baz baz (Baz) foo;参数 interfaces –应该实现的额外接口。 返回设置实例以便您可以流畅地指定其他设置 现在让我们看一下测试 PlayerServiceImplTest.java package com.blogspot.toomuchcoding.service;import com.blogspot.toomuchcoding.factory.PlayerFactory;
import com.blogspot.toomuchcoding.model.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.*;/*** User: mgrzejszczak* Date: 08.06.13* Time: 19:26*/
RunWith(MockitoJUnitRunner.class)
public class PlayerServiceImplTest {MockPlayerFactory playerFactory;InjectMocksPlayerServiceImpl objectUnderTest;Mock(extraInterfaces {DJ.class, JavaDeveloper.class})DefensivePlayer defensivePlayerWithDjAndJavaDevSkills;MockDefensivePlayer defensivePlayer;MockOffensivePlayer offensivePlayer;MockPlayer commonPlayer;Testpublic void shouldReturnOffensivePlayerThatRan() throws Exception {//givengiven(playerFactory.createPlayer(PositionType.ATT)).willReturn(offensivePlayer);//whenPlayer createdPlayer objectUnderTest.playAGameWithAPlayerOfPosition(PositionType.ATT);//thenassertThat(createdPlayer offensivePlayer, is(true));verify(offensivePlayer).run();}Testpublic void shouldReturnDefensivePlayerButHeWontBeADjNorAJavaDev() throws Exception {//givengiven(playerFactory.createPlayer(PositionType.GK)).willReturn(defensivePlayer);//whenPlayer createdPlayer objectUnderTest.playAGameWithAPlayerOfPosition(PositionType.GK);//thenassertThat(createdPlayer defensivePlayer, is(true));verify(defensivePlayer).run();verify(defensivePlayer).defend();verifyNoMoreInteractions(defensivePlayer);}Testpublic void shouldReturnDefensivePlayerBeingADjAndAJavaDev() throws Exception {//givengiven(playerFactory.createPlayer(PositionType.GK)).willReturn(defensivePlayerWithDjAndJavaDevSkills);doAnswer(new AnswerObject() {Overridepublic Object answer(InvocationOnMock invocationOnMock) throws Throwable {System.out.println(Hit me baby one more time!);return null;}}).when(((DJ) defensivePlayerWithDjAndJavaDevSkills)).playSomeMusic();doAnswer(new AnswerObject() {Overridepublic Object answer(InvocationOnMock invocationOnMock) throws Throwable {System.out.println(public static void main(String... args){\n});return null;}}).when(((JavaDeveloper) defensivePlayerWithDjAndJavaDevSkills)).doSomeSeriousCoding();//whenPlayer createdPlayer objectUnderTest.playAGameWithAPlayerOfPosition(PositionType.GK);//thenassertThat(createdPlayer defensivePlayerWithDjAndJavaDevSkills, is(true));verify(defensivePlayerWithDjAndJavaDevSkills).run();verify(defensivePlayerWithDjAndJavaDevSkills).defend();verify((DJ) defensivePlayerWithDjAndJavaDevSkills).playSomeMusic();verify((JavaDeveloper) defensivePlayerWithDjAndJavaDevSkills).doSomeSeriousCoding();}Testpublic void shouldReturnDefensivePlayerBeingADjAndAJavaDevByUsingWithSettings() throws Exception {//givenDefensivePlayer defensivePlayerWithDjAndJavaDevSkills mock(DefensivePlayer.class, withSettings().extraInterfaces(DJ.class, JavaDeveloper.class));given(playerFactory.createPlayer(PositionType.GK)).willReturn(defensivePlayerWithDjAndJavaDevSkills);doAnswer(new AnswerObject() {Overridepublic Object answer(InvocationOnMock invocationOnMock) throws Throwable {System.out.println(Hit me baby one more time!);return null;}}).when(((DJ) defensivePlayerWithDjAndJavaDevSkills)).playSomeMusic();doAnswer(new AnswerObject() {Overridepublic Object answer(InvocationOnMock invocationOnMock) throws Throwable {System.out.println(public static void main(String... args){\n});return null;}}).when(((JavaDeveloper) defensivePlayerWithDjAndJavaDevSkills)).doSomeSeriousCoding();//whenPlayer createdPlayer objectUnderTest.playAGameWithAPlayerOfPosition(PositionType.GK);//thenassertThat(createdPlayer defensivePlayerWithDjAndJavaDevSkills, is(true));verify(defensivePlayerWithDjAndJavaDevSkills).run();verify(defensivePlayerWithDjAndJavaDevSkills).defend();verify((DJ) defensivePlayerWithDjAndJavaDevSkills).playSomeMusic();verify((JavaDeveloper) defensivePlayerWithDjAndJavaDevSkills).doSomeSeriousCoding();}Testpublic void shouldReturnCommonPlayer() throws Exception {//givengiven(playerFactory.createPlayer(null)).willReturn(commonPlayer);//whenPlayer createdPlayer objectUnderTest.playAGameWithAPlayerOfPosition(null);//thenassertThat(createdPlayer, is(commonPlayer));}
} 这里有很多测试所以让我们看一下最有趣的测试。 但是在开始之前我们先提供RunWithMockitoJUnitRunner.class批注这使我们能够使用Mockito批注例如Mock和InjectMocks 。 说到哪个Mock注释会创建一个Mock而InjectMocks则通过构造函数或setter注入所有模拟这太棒了吗。 对于防御性玩家我们使用注记extraInterfaces的extra元素该元素为给定的Mock提供其他接口。 您还可以编写可以在shouldReturnDefensivePlayerBeingADjAndAJavaDevByUsingWithSettings测试中找到的内容 DefensivePlayer defensivePlayerWithDjAndJavaDevSkills mock(DefensivePlayer.class, withSettings().extraInterfaces(DJ.class, JavaDeveloper.class)); 让我们仔细看看为与DefensivePlayer相关的功能和测试功能的转换部分编写的测试 Testpublic void shouldReturnDefensivePlayerBeingADjAndAJavaDev() throws Exception {//givengiven(playerFactory.createPlayer(PositionType.GK)).willReturn(defensivePlayerWithDjAndJavaDevSkills);doAnswer(new AnswerObject() {Overridepublic Object answer(InvocationOnMock invocationOnMock) throws Throwable {System.out.println(Hit me baby one more time!);return null;}}).when(((DJ) defensivePlayerWithDjAndJavaDevSkills)).playSomeMusic();doAnswer(new AnswerObject() {Overridepublic Object answer(InvocationOnMock invocationOnMock) throws Throwable {System.out.println(public static void main(String... args){\n});return null;}}).when(((JavaDeveloper) defensivePlayerWithDjAndJavaDevSkills)).doSomeSeriousCoding();//whenPlayer createdPlayer objectUnderTest.playAGameWithAPlayerOfPosition(PositionType.GK);//thenassertThat(createdPlayer defensivePlayerWithDjAndJavaDevSkills, is(true));verify(defensivePlayerWithDjAndJavaDevSkills).run();verify(defensivePlayerWithDjAndJavaDevSkills).defend();verify((DJ) defensivePlayerWithDjAndJavaDevSkills).playSomeMusic();verify((JavaDeveloper) defensivePlayerWithDjAndJavaDevSkills).doSomeSeriousCoding();} 我们正在使用BDDMockito静态方法如给定...。willReturn...。willAnswer...等。然后将空方法与自定义Anwsers结合使用。 在下一行中您可以看到为了存根另一个接口的方法必须将模拟转换为给定的接口。 这与验证阶段有关在该阶段我不检查是否已执行方法您必须将模拟转换为给定的接口。 您可以通过从工厂返回一个真实的实现来改进测试或者如果创建一个繁琐的操作则可以返回这种实现的模拟。 我想在这里展示的是如何在Mockito中使用额外的接口也许我的用例不是最好的用例。 无论如何测试中提出的实现都是不好的所以我们应该考虑重构它的方法…… 一种想法可能是假设在Service中完成的附加逻辑是对象创建的一部分将代码原样移至工厂 PlayFactoryImplWithFieldSettingLogic.java package com.blogspot.toomuchcoding.factory;import com.blogspot.toomuchcoding.adapter.CommonPlayerAdapter;
import com.blogspot.toomuchcoding.adapter.DefencePlayerAdapter;
import com.blogspot.toomuchcoding.adapter.OffensivePlayerAdapter;
import com.blogspot.toomuchcoding.model.*;/*** User: mgrzejszczak* Date: 09.06.13* Time: 15:53*/public class PlayerFactoryImplWithFieldSettingLogic implements PlayerFactory {Overridepublic Player createPlayer(PositionType positionType) {PlayerDetails player createCommonPlayer(positionType);switch (positionType){case ATT:return createOffensivePlayer(player);case MID:return createOffensivePlayer(player);case DEF:return createDefensivePlayer(player);case GK:return createDefensivePlayer(player);default:return new CommonPlayerAdapter(player);}}private Player createDefensivePlayer(PlayerDetails player) {DefencePlayerAdapter defencePlayerAdapter new DefencePlayerAdapter(player);defencePlayerAdapter.defend();defencePlayerAdapter.playSomeMusic();defencePlayerAdapter.doSomeSeriousCoding();return defencePlayerAdapter;}private OffensivePlayer createOffensivePlayer(PlayerDetails player) {OffensivePlayer offensivePlayer new OffensivePlayerAdapter(player);offensivePlayer.shoot();return offensivePlayer;}private PlayerDetails createCommonPlayer(PositionType positionType){PlayerDetails playerDetails new PlayerDetails();playerDetails.setPosition(positionType);return playerDetails;}
} 这样就没有强制转换代码是干净的。 现在PlayerService如下所示 PlayerServiceImplWIthoutUnnecessaryLogic.java package com.blogspot.toomuchcoding.service;import com.blogspot.toomuchcoding.factory.PlayerFactory;
import com.blogspot.toomuchcoding.model.*;/*** User: mgrzejszczak* Date: 08.06.13* Time: 19:02*/
public class PlayerServiceImplWithoutUnnecessaryLogic implements PlayerService {private PlayerFactory playerFactory;/*** Whats the point in having this method then?* param positionType* return*/Overridepublic Player playAGameWithAPlayerOfPosition(PositionType positionType) {return playerFactory.createPlayer(positionType);}public PlayerFactory getPlayerFactory() {return playerFactory;}public void setPlayerFactory(PlayerFactory playerFactory) {this.playerFactory playerFactory;}
} 随之而来的问题是您的代码库中是否甚至需要这种方法…… 总结一下我希望我能展示如何 使用MockitoJUnitRunner以干净的方式注入模拟 使用注释或静态方法添加可以由您的模拟使用的额外接口 使用BDDMockito执行方法存根 带有自定义答案的存根无效方法 附加接口的存根和验证方法 重构使用类强制转换的代码 来源可从TooMuchCoding Bitbucket存储库和TooMuchCoding Github存储库中获得。 参考 Mockito –来自我们的JCG合作伙伴 Marcin Grzejszczak位于Blog上的 具有注释和静态方法的Extra Interfaces 用于编码瘾君子博客。 翻译自: https://www.javacodegeeks.com/2013/06/mockito-extra-interfaces-with-annotations-and-static-methods.htmlmockito 静态方法