java - Hamcrest:测试列表包含一个具有特定值的私有(private)字段的项目

标签 java unit-testing junit hamcrest

我有这门课:

public class A {
    private int x;

    public A(int x) {
        this.x = x;
    }
}

我想测试另一个类中的方法:

public class B {
    public List underTest() {
        int x = doStuff();
        return Collections.singletonList(new A(x));
    }

    private int doStuff() { /* ... */ }
}

我想测试一下,在 underTest() 的末尾,返回列表中的项目包含一个等于特定值的 x 字段。我写了这个:

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.is;

@Test
public void Test1() {
    List result = bInstance.underTest();
    assertThat(result, contains(hasProperty("x", is(1))));
}

但是 junit 提示我的测试用例item 0: No Property "x"

我该如何测试这个?我唯一能想到的就是为 getX() 添加一个公共(public) getter,然后迭代返回的 List 并检查每个元素。请记住,虽然该方法返回 singletonList,但返回类型只是 List,因此将来可以更改为具有多个值就在其中。

最佳答案

我认为首先值得一提的是,像这样测试类内部并不是一个好主意,除非在非常特殊的情况下。您的测试将很脆弱,通常完全安全的更改(即重命名字段)现在可能会导致您的自动构建失败。您应该测试外部行为,而不是实现细节。

看来你最好在类A中实现equalshashCode,所以你可以简单地这样做:

contains(new A(1))

话虽如此,如果您确实有充分的理由这样做(这种情况很少见),那么您就不能使用 hasProperty 来执行此操作。

来自the JavaDoc :

Creates a matcher that matches when the examined object has a JavaBean property with the specified name whose value satisfies the specified matcher.

我相信这意味着您需要一个名为 getX 的方法。

您不应该仅仅为了测试目的而添加这样的方法,但您可以编写自己的通用Matcher将使用反射来检查类的字段的实现。

这是一个示例实现:

class ReflectiveFieldMatcher<T> extends BaseMatcher<Object>
{
    private final String fieldName;
    private final T expectedValue;

    ReflectiveFieldMatcher(final String fieldName, final T expectedValue)
    {
        this.fieldName = fieldName;
        this.expectedValue = expectedValue;
    }

    @Override
    public boolean matches(final Object obj)
    {
        for (final Field field : obj.getClass().getFields())
        {
            if (field.getName().equals(fieldName))
            {
                field.setAccessible(true);
                try
                {
                    Object value = field.get(obj);
                    return expectedValue.equals(value);
                }
                catch (final IllegalAccessException e)
                {
                    throw new RuntimeException(e);
                }
            }
        }
        return false;
    }

    @Override
    public void describeTo(final Description description)
    {
        description.appendText("Object with field '" + fieldName + "' with value: " + expectedValue);
    }
}

您的示例现在如下所示:

assertThat(result, contains(new ReflectiveFieldMatcher<>("x", 1)));

关于java - Hamcrest:测试列表包含一个具有特定值的私有(private)字段的项目,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54316818/

相关文章:

java - 具有多个注释的方法的 AspectJ 切入点

java - 外部小程序上的 MouseMove

angularjs - 使用 Angular 2 模拟组件

android - Android 应用程序中的单元测试。我该怎么办?

java - 是否可以在进程中启动 Zookeeper 服务器实例,例如用于单元测试?

java - 如何使用单例类实现依赖注入(inject)

java - 在 Eclipse 中进行 JUnit 测试之前运行外部命令

java - 使用表单 :input 编写 Spring MVC 表单的正确方法是什么

java - 为什么 JUnit 方法引用对象?

java - 为什么安装jdk 1.7后没有创建系统变量(路径)