JUnit的内置Hamcrest Core Matcher支持

用JUnit和Hamcrest改进assertEquals的文章中,我简要讨论了Hamcrest核心 ”匹配器与JUnit的现代版本“结合”在一起的情况。 在那篇文章中,我特别关注了JUnit的assertThat(T,Matcher)静态方法与Hamcrest核心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));
   }

尽管不是必需的,但一些开发人员喜欢将isequalTo一起使用,因为它对他们来说更流利。 这就是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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,需要在Eclipse中添加JUnitHamcrest和Mockito的依赖库。可以通过以下步骤完成: 1. 在Eclipse的Package Explorer中右键单击项目,选择Properties。 2. 在弹出的对话框中选择Java Build Path,然后选择Libraries。 3. 单击Add Library,然后选择JUnitHamcrest和Mockito。 完成后,可以开始编写集成测试代码。以下是一个示例代码,使用Hamcrest和Mockito来测试一个简单的服务: ```java import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; import static org.mockito.Mockito.*; import org.junit.Before; import org.junit.Test; public class MyServiceIntegrationTest { private MyService myService; private MyDependency myDependency; @Before public void setup() { myDependency = mock(MyDependency.class); myService = new MyService(myDependency); } @Test public void testDoSomething() { // Given when(myDependency.getData()).thenReturn("data"); // When String result = myService.doSomething(); // Then assertThat(result, is("data processed")); verify(myDependency).getData(); } } ``` 在这个例子中,MyServiceIntegrationTest 类测试了 MyService 类与 MyDependency 类的集成。在 `setup()` 方法中,我们使用 Mockito 创建了一个 MyDependency 的 mock 对象,并将其传递给 MyService。在 `testDoSomething()` 方法中,我们使用 Hamcrest 断言测试 MyService 的 `doSomething()` 方法是否正确处理了 MyDependency 返回的数据。最后,我们使用 Mockito 验证了 MyDependency 的 `getData()` 方法是否被调用了一次。 可以使用 JUnit 运行这个测试类,通过右键单击类名并选择 Run As > JUnit Test 来运行测试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值