JUnit的内置Hamcrest Core Matcher支持
is()
匹配器的结合使用,该方法自动包含在更高版本的JUnit中。 在本文中,我将介绍与最新版本的JUnit捆绑在一起的其他Hamcrest“核心”匹配器。 JUnit的包括开箱即用的Hamcrest “核心”匹配器的两个优点是,无需专门下载Hamcrest,也无需在单元测试类路径中明确包含它。 在查看更多方便的Hamcrest“核心”匹配器之前,重要的是要在此指出,我有意重复提及“核心” Hamcrest匹配器,因为JUnit的最新版本仅提供“核心”( 并非全部 )Hamcrest匹配器自动。 仍然需要单独下载核心匹配器之外的任何Hamcrest匹配器,并在单元测试类路径上明确指定。 了解什么是Hamcrest“核心”(以及在最新版本的JUnit中默认情况下可用的匹配器)的一种方法是查看该程序包的基于Javadoc的API文档 :
从JUnit提供的org.hamcrest.core
软件包的文档中,我们可以找到以下匹配器(及其描述):
类 | Javadoc类说明 | 盖在这里? |
---|---|---|
AllOf <T> | 计算两个匹配器的逻辑和。 | 是 |
AnyOf <T> | 计算两个匹配器的逻辑和。 | 是 |
DescribedAs <T> | 为另一个匹配器提供自定义描述。 | 是 |
是<T> | 装饰另一个Matcher,保留其行为,但允许测试更具表现力。 | 再次 |
IsAnything <T> | 始终返回true的匹配器。 | 没有 |
IsEqual <T> | 根据Object.equals(java.lang.Object)invokedMethod测试,该值是否等于另一个值? | 是 |
IsInstanceOf | 测试值是否为类的实例。 | 是 |
IsNot <T> | 计算匹配器的逻辑取反。 | 是 |
IsNull <T> | 值是否为空? | 是 |
IsSame <T> | 该值与另一个值是同一对象吗? | 是 |
在我先前的演示Hamcrest is()
匹配器与JUnit的assertThat()
结合使用的文章中,我使用了IntegerArithmetic
实现作为测试文件。 我将在这里再次使用它来演示其他一些Hamcrest核心匹配器。 为了方便起见,该类在下面复制。
IntegerArithmetic.java
package dustin.examples; /** * Simple class supporting integer arithmetic. * * @author Dustin */ public class IntegerArithmetic { /** * Provide the product of the provided integers. * * @param firstInteger First integer to be multiplied. * @param secondInteger Second integer to be multiplied. * @param integers Integers to be multiplied together for a product. * @return Product of the provided integers. * @throws ArithmeticException Thrown in my product is too small or too large * to be properly represented by a Java integer. */ public int multiplyIntegers( final int firstInteger, final int secondInteger, final int ... integers) { int returnInt = firstInteger * secondInteger; for (final int integer : integers) { returnInt *= integer; } return returnInt; } }
在“ 使用JUnit和Hamcrest改进assertEquals”中 ,我主要依靠is()
来将预期结果与测试的整数乘法的实际结果进行比较。 另一个选择是使用equalTo
匹配器,如下面的代码清单所示。
使用Hamcrest equalTo()
/** * Test of multiplyIntegers method, of class IntegerArithmetic, using core * Hamcrest matcher equalTo. */ @Test public void testWithJUnitHamcrestEqualTo() { final int[] integers = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; final int expectedResult = 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 *13 * 14 * 15; final int result = this.instance.multiplyIntegers(2, 3, integers); assertThat(result, equalTo(expectedResult)); }
尽管不是必需的,但一些开发人员喜欢将is
和equalTo
一起使用,因为它对他们来说更流利。 这就是is
存在的原因:更流畅地使用其他匹配器。 我经常equalTo()
使用is()
(暗示equalTo()
),如通过JUnit和Hamcrest改善对assertEquals所述 。 下一个示例演示了将is()
匹配器与equalTo
匹配器结合使用。
将Hamcrest equalTo()与is()结合使用
/** * Test of multiplyIntegers method, of class IntegerArithmetic, using core * Hamcrest matcher equalTo with "is" Matcher.. */ @Test public void testWithJUnitHamcrestEqualToAndIsMatchers() { final int[] integers = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; final int expectedResult = 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 *13 * 14 * 15; final int result = this.instance.multiplyIntegers(2, 3, integers); assertThat(result, is(equalTo(expectedResult))); }
equalTo
Hamcrest匹配器执行类似于调用Object.equals(Object)的比较。 实际上,其比较功能依赖于底层对象的equals(Object)
实现的使用。 这意味着最后两个示例将通过,因为所比较的数字在逻辑上是等效的。 当想要确保更大的身份相等性时(实际上是相同的对象,而不仅仅是相同的逻辑内容),可以使用Hamcrest sameInstance
匹配器,如下面的代码清单所示。 因为断言是正确的,所以也应用了not
匹配器,因为只有预期的和实际的结果并非相同,实例才通过测试,只有“否”通过测试。
使用Hamcrest sameInstance()和not()
/** * Test of multiplyIntegers method, of class IntegerArithmetic, using core * Hamcrest matchers not and sameInstance. */ @Test public void testWithJUnitHamcrestNotSameInstance() { final int[] integers = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; final int expectedResult = 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 *13 * 14 * 15; final int result = this.instance.multiplyIntegers(2, 3, integers); assertThat(result, not(sameInstance(expectedResult))); }
有时需要控制从失败的单元测试的断言中输出的文本。 JUnit包括核心Hamcrest匹配器asDescribed()
来支持此功能。 下一个清单中显示了这样的代码示例,代码清单后的NetBeans IDE的屏幕快照中显示了失败测试(和相应的断言)的输出。
将Hamcrest asDescribed()与sameInstance()一起使用
/** * Test of multiplyIntegers method, of class IntegerArithmetic, using core * Hamcrest matchers sameInstance and asDescribed. This one will assert a * failure so that the asDescribed can be demonstrated (don't do this with * your unit tests as home)! */ @Test public void testWithJUnitHamcrestSameInstanceDescribedAs() { final int[] integers = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; final int expectedResult = 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 *13 * 14 * 15; final int result = this.instance.multiplyIntegers(2, 3, integers); assertThat(result, describedAs( "Not same object (different identity reference)", sameInstance(expectedResult))); }
当关联的单元测试断言失败时,使用describedAs()
可以报告更有意义的消息。
现在,我将使用另一个人为设计的类来帮助说明在最新版本的JUnit中可用的其他核心Hamcrest匹配器。 接下来显示“需要测试”。
SetFactory.java
package dustin.examples; import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; /** * A Factory that provides an implementation of a Set interface based on * supplied SetType that indicates desired type of Set implementation. * * @author Dustin */ public class SetFactory<T extends Object> { public enum SetType { ENUM(EnumSet.class), HASH(HashSet.class), SORTED(SortedSet.class), // SortedSet is an interface, not implementation TREE(TreeSet.class), RANDOM(Set.class); // Set is an interface, not a concrete collection private Class setTypeImpl = null; SetType(final Class newSetType) { this.setTypeImpl = newSetType; } public Class getSetImplType() { return this.setTypeImpl; } } private SetFactory() {} public static SetFactory newInstance() { return new SetFactory(); } /** * Creates a Set using implementation corresponding to the provided Set Type * that has a generic parameterized type of that specified. * * @param setType Type of Set implementation to be used. * @param parameterizedType Generic parameterized type for the new set. * @return Newly constructed Set of provided implementation type and using * the specified generic parameterized type; null if either of the provided * parameters is null. * @throws ClassCastException Thrown if the provided SetType is SetType.ENUM, * but the provided parameterizedType is not an Enum. */ public Set<T> createSet( final SetType setType, final Class<T> parameterizedType) { if (setType == null || parameterizedType == null) { return null; } Set<T> newSet = null; try { switch (setType) { case ENUM: if (parameterizedType.isEnum()) { newSet = EnumSet.noneOf((Class<Enum>)parameterizedType); } else { throw new ClassCastException( "Provided SetType of ENUM being supplied with " + "parameterized type that is not an enum [" + parameterizedType.getName() + "]."); } break; case RANDOM: newSet = LinkedHashSet.class.newInstance(); break; case SORTED: newSet = TreeSet.class.newInstance(); break; default: newSet = (Set<T>) setType.getSetImplType().getConstructor().newInstance(); break; } } catch ( InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException ex) { Logger.getLogger(SetFactory.class.getName()).log(Level.SEVERE, null, ex); } return newSet; } }
刚刚显示了代码的人为设计的类提供了使用其他Hamcrest“核心”匹配器的机会。 如上所述,可以将所有这些匹配与is
匹配器一起使用,以提高语句的流畅性。 两个有用的“核心”的匹配是nullValue()
和notNullValue()
这两者在未来基于JUnit的代码列表被证实(和is
结合使用在一种情况下)。
使用Hamcrest nullValue()和notNullValue()
/** * Test of createSet method, of class SetFactory, with null SetType passed. */ @Test public void testCreateSetNullSetType() { final SetFactory factory = SetFactory.newInstance(); final Set<String> strings = factory.createSet(null, String.class); assertThat(strings, nullValue()); } /** * Test of createSet method, of class SetFactory, with null parameterized type * passed. */ @Test public void testCreateSetNullParameterizedType() { final SetFactory factory = SetFactory.newInstance(); final Set<String> strings = factory.createSet(SetType.TREE, null); assertThat(strings, is(nullValue())); } @Test public void testCreateTreeSetOfStringsNotNullIfValidParams() { final SetFactory factory = SetFactory.newInstance(); final Set<String> strings = factory.createSet(SetType.TREE, String.class); assertThat(strings, notNullValue()); }
Hamcrest匹配器instanceOf
也很有用,并在下一个代码清单中进行演示(一个单独使用instanceOf
的示例和一个与is
结合使用的示例)。
使用Hamcrest instanceOf()
@Test public void testCreateTreeSetOfStringsIsTreeSet() { final SetFactory factory = SetFactory.newInstance(); final Set<String> strings = factory.createSet(SetType.TREE, String.class); assertThat(strings, is(instanceOf(TreeSet.class))); } @Test public void testCreateEnumSet() { final SetFactory factory = SetFactory.newInstance(); final Set<RoundingMode> roundingModes = factory.createSet(SetType.ENUM, RoundingMode.class); roundingModes.add(RoundingMode.UP); assertThat(roundingModes, instanceOf(EnumSet.class)); }
到目前为止,许多Hamcrest核心匹配器都提供了更高的流畅性和可读性,但出于更多原因,我喜欢接下来的两个匹配器。 Hamcrest hasItem()
匹配器检查集合中指定项的存在,甚至更有用的Hamcrest hasItems()
匹配器检查集合中多个规定项的存在。 在代码中更容易看到这一点,下面的代码演示了它们的实际作用。
使用Hamcrest hasItem()和hasItems()
@Test public void testCreateTreeSetOfStringsHasOneOfAddedStrings() { final SetFactory factory = SetFactory.newInstance(); final Set<String> strings = factory.createSet(SetType.TREE, String.class); strings.add("Tucson"); strings.add("Arizona"); assertThat(strings, hasItem("Tucson")); } @Test public void testCreateTreeSetOfStringsHasAllOfAddedStrings() { final SetFactory factory = SetFactory.newInstance(); final Set<String> strings = factory.createSet(SetType.TREE, String.class); strings.add("Tucson"); strings.add("Arizona"); assertThat(strings, hasItems("Tucson", "Arizona")); }
有时需要测试某种测试方法的结果,以确保它满足各种各样的期望。 这就是Hamcrest allOf
匹配器派上用场的地方。 此匹配器确保所有条件(表示为匹配器)都为真。 在下面的代码清单中对此进行了说明,该清单使用一个断言测试生成的Set
不为null,其中包含两个特定的String,并且它是TreeSet
的实例。
使用Hamcrest allOf()
@Test public void testCreateSetAllKindsOfGoodness() { final SetFactory factory = SetFactory.newInstance(); final Set<String> strings = factory.createSet(SetType.TREE, String.class); strings.add("Tucson"); strings.add("Arizona"); assertThat( strings, allOf( notNullValue(), hasItems("Tucson", "Arizona"), instanceOf(TreeSet.class))); }
为了演示Hamcrest核心“ anyOf”匹配器为JUnit的较新版本提供了开箱即用的功能,我将使用另一个需要单元测试的可笑的Java类。
今天.java
package dustin.examples; import java.util.Calendar; import java.util.Locale; /** * Provide what day of the week today is. * * @author Dustin */ public class Today { /** * Provide the day of the week of today's date. * * @return Integer representing today's day of the week, corresponding to * static fields defined in Calendar class. */ public int getTodayDayOfWeek() { return Calendar.getInstance(Locale.US).get(Calendar.DAY_OF_WEEK); } }
现在,我需要测试上述类中的唯一方法是否正确返回了代表星期几的有效整数。 我希望我的测试确保返回代表星期天到星期六的一天的有效整数,但是要测试的方法是这样的,使得它可能与任何给定的测试运行都不是一周中的同一天。 下面的代码清单指示如何使用包含JUnit的Hamcrest“ anyOf”匹配器进行测试。
使用Hamcrest anyOf()
/** * Test of getTodayDayOfWeek method, of class Today. */ @Test public void testGetTodayDayOfWeek() { final Today instance = new Today(); final int todayDayOfWeek = instance.getTodayDayOfWeek(); assertThat(todayDayOfWeek, describedAs( "Day of week not in range.", anyOf(is(Calendar.SUNDAY), is(Calendar.MONDAY), is(Calendar.TUESDAY), is(Calendar.WEDNESDAY), is(Calendar.THURSDAY), is(Calendar.FRIDAY), is(Calendar.SATURDAY)))); }
尽管Hamcrest的allOf
要求所有条件都必须匹配才能避免断言,但任何一个条件的存在足以确保anyOf
不会导致失败的断言。
我最喜欢的确定JUnit可用的Hamcrest核心匹配器的方法是在Java IDE中使用导入完成。 当我静态导入org.hamcrest.CoreMatchers.*
软件包内容时,将显示所有可用的匹配器。 我可以在IDE中查看*
代表什么,以查看对我可用的匹配器。
JUnit包含Hamcrest“核心”匹配器真是太好了,这篇文章试图演示其中的大多数。 Hamcrest在“核心”之外提供了许多有用的匹配器,它们也很有用。 有关详细信息,请参见Hamcrest教程 。
参考:在Inspired by Actual Events博客上,我们的JCG合作伙伴 Dustin Marx提供了JUnit的内置Hamcrest Core Matcher支持 。
翻译自: https://www.javacodegeeks.com/2012/06/junits-built-in-hamcrest-core-matcher.html