很抱歉没有写一段时间,但是我正忙于为DZone编写JBoss Drools Refcard,而且我正在写一本有关Mockito的书,因此我没有太多时间来写博客了……
无论如何,最近在我当前的项目中,我对使用Mockito和JAXB结构进行单元测试有一个有趣的情况。 我们已经从为我们提供的模式生成的嵌套了非常深的JAXB结构,这意味着我们无论如何都无法更改它。
让我们看一下项目结构:
项目结构非常简单–有一个Player.xsd模式文件,该文件由于使用了jaxb2-maven-plugin生成了与目标/ jaxb /文件夹中定义的相应包中的模式相对应的生成的JAXB Java类。 pom.xml 。 说到其中,让我们看一下pom.xml文件。
pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.blogspot.toomuchcoding</groupId><artifactId>mockito-deep_stubs</artifactId><version>0.0.1-SNAPSHOT</version><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.6</maven.compiler.source><maven.compiler.target>1.6</maven.compiler.target></properties><repositories><repository><id>spring-release</id><url>http://maven.springframework.org/release</url></repository><repository><id>maven-us-nuxeo</id><url>https://maven-us.nuxeo.org/nexus/content/groups/public</url></repository></repositories><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.10</version></dependency><dependency><groupId>org.mockito</groupId><artifactId>mockito-all</artifactId><version>1.9.5</version><scope>test</scope></dependency></dependencies><build><pluginManagement><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>2.5.1</version></plugin></plugins></pluginManagement><plugins><plugin><groupId>org.codehaus.mojo</groupId><artifactId>jaxb2-maven-plugin</artifactId><version>1.5</version><executions><execution><id>xjc</id><goals><goal>xjc</goal></goals></execution></executions><configuration><packageName>com.blogspot.toomuchcoding.model</packageName><schemaDirectory>${project.basedir}/src/main/resources/xsd</schemaDirectory></configuration></plugin></plugins></build></project>
除了先前定义的项目依赖关系外,如先前在配置节点中的jaxb2-maven-plugin中所述,您还可以基于schemaDirectory值定义packageName值,该值定义将JAXB类生成到哪个程序包,插件可以在其中找到适当的架构文件。
说到这,让我们检查一下Player.xsd模式文件( 类似于我的Spring JMS自动消息转换文章中提供的文件 ):
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"><xsd:element name="PlayerDetails"><xsd:complexType><xsd:sequence><xsd:element name="Name" type="xsd:string"/><xsd:element name="Surname" type="xsd:string"/><xsd:element name="Position" type="PositionType"/><xsd:element name="Age" type="xsd:int"/><xsd:element name="ClubDetails" type="ClubDetails"/></xsd:sequence></xsd:complexType></xsd:element><xsd:complexType name="ClubDetails"><xsd:sequence><xsd:element name="TeamName" type="xsd:string"/><xsd:element name="Country" type="CountryDetails"/></xsd:sequence></xsd:complexType><xsd:complexType name="CountryDetails"><xsd:sequence><xsd:element name="CountryName" type="xsd:string"/><xsd:element name="CountryCode" type="CountryCodeDetails"/></xsd:sequence></xsd:complexType><xsd:complexType name="CountryCodeDetails"><xsd:sequence><xsd:element name="CountryName" type="xsd:string"/><xsd:element name="CountryCode" type="CountryCodeType"/></xsd:sequence></xsd:complexType><xsd:simpleType name="CountryCodeType"><xsd:restriction base="xsd:string"><xsd:enumeration value="PL"/><xsd:enumeration value="GER"/><xsd:enumeration value="FRA"/><xsd:enumeration value="ENG"/><xsd:enumeration value="ESP"/></xsd:restriction></xsd:simpleType><xsd:simpleType name="PositionType"><xsd:restriction base="xsd:string"><xsd:enumeration value="GK"/><xsd:enumeration value="DEF"/><xsd:enumeration value="MID"/><xsd:enumeration value="ATT"/></xsd:restriction></xsd:simpleType></xsd:schema>
如您所见,我正在定义一些复杂的类型,即使它们可能没有商业意义,但您可以在现实生活中找到这样的例子。
让我们找出我们要测试的方法的外观。 在这里,我们有PlayerServiceImpl实现了PlayerService接口:
package com.blogspot.toomuchcoding.service;import com.blogspot.toomuchcoding.model.PlayerDetails;/*** User: mgrzejszczak* Date: 08.06.13* Time: 19:02*/
public class PlayerServiceImpl implements PlayerService {@Overridepublic boolean isPlayerOfGivenCountry(PlayerDetails playerDetails, String country) {String countryValue = playerDetails.getClubDetails().getCountry().getCountryCode().getCountryCode().value();return countryValue.equalsIgnoreCase(country);}
}
我们从JAXB生成的类中获取嵌套元素。 尽管它违反了Demeter的定律,但调用结构的方法却很常见,因为JAXB生成的类实际上是结构,因此,我完全同意Martin Fowler的观点,即应将其称为Demeter的建议 。 无论如何,让我们看看如何测试该方法:
@Testpublic void shouldReturnTrueIfCountryCodeIsTheSame() throws Exception {//givenPlayerDetails playerDetails = new PlayerDetails();ClubDetails clubDetails = new ClubDetails();CountryDetails countryDetails = new CountryDetails();CountryCodeDetails countryCodeDetails = new CountryCodeDetails();playerDetails.setClubDetails(clubDetails);clubDetails.setCountry(countryDetails);countryDetails.setCountryCode(countryCodeDetails);countryCodeDetails.setCountryCode(CountryCodeType.ENG);//whenboolean playerOfGivenCountry = objectUnderTest.isPlayerOfGivenCountry(playerDetails, COUNTRY_CODE_ENG);//thenassertThat(playerOfGivenCountry, is(true));}
该函数检查是否具有相同的国家(地区)代码,是否从该方法中获取了一个真正的布尔值。 唯一的问题是要创建输入消息时发生的集合和实例化的数量。 在我们的项目中,嵌套元素的数量是原来的两倍,因此您只能想象创建输入对象所需的代码数量…
那么如何改善此代码呢? Mockito与Mockito.mock(…)方法的RETURN_DEEP_STUBS默认答案一起出手 :
@Testpublic void shouldReturnTrueIfCountryCodeIsTheSameUsingMockitoReturnDeepStubs() throws Exception {//givenPlayerDetails playerDetailsMock = mock(PlayerDetails.class, RETURNS_DEEP_STUBS);CountryCodeType countryCodeType = CountryCodeType.ENG;when(playerDetailsMock.getClubDetails().getCountry().getCountryCode().getCountryCode()).thenReturn(countryCodeType);//whenboolean playerOfGivenCountry = objectUnderTest.isPlayerOfGivenCountry(playerDetailsMock, COUNTRY_CODE_ENG);//thenassertThat(playerOfGivenCountry, is(true));}
因此,这里发生的是您使用Mockito.mock(…)方法并提供了RETURNS_DEEP_STUBS答案,该答案将为您自动创建模拟 。 请注意,不能嘲笑枚举,这就是为什么您不能在Mockito.when(…)函数playerDetailsMock.getClubDetails()。getCountry()。getCountryCode()。getCountryCode()。getValue()中编写代码的原因。
总结一下,您可以比较两个测试的可读性,并通过使用Mockito RETURNS_DEEP_STUBS默认答案来了解使用JAXB结构有多清晰。
当然,该示例的资源可从BitBucket和GitHub获得 。
翻译自: https://www.javacodegeeks.com/2013/07/mockito-returns_deep_stubs-for-jaxb.html