首先,请注意IterableSubject.containsExactly()
断言输入“完全包含提供的对象或失败”。这意味着 - 即使您可以Matcher
在此处传递对象 - 我们正在断言列表仅包含一个ExpectedType
实例。现有答案都没有正确强制执行该不变量(相反,heenenee 的方法断言一个实例ExpectedType
和任意数量的其他实例,并且您的解决方案断言该列表包含任意数量的实例ExpectedType
)。当我阅读您的问题时,您确实打算断言完全一致的属性,但无论如何这表明已接受的解决方案存在问题 - 它可能会意外地导致您不打算做出的断言。
当我遇到这样的 Truth API 的限制时,我总是尝试的第一件事就是简单地将断言拆分为单独的步骤。事实证明,这通常易于编写、易于阅读并且通常是防错的。可以理解的是,人们经常试图用 Truth 寻找优雅的单行语句,但一般来说,进行顺序断言并没有错。
在这里很难击败该策略:
assertThat(ls).hasSize(1);
assertThat(Iterables.getOnlyElement(ls)).isInstanceOf(String.class);
如果 iterable 的大小不是 1,我们将收到一条错误消息,告诉我们(以及 iterable 的内容)。如果是,我们断言唯一的元素是 的一个实例String
。完毕!
对于n实例的一般情况,代码确实会变得有点混乱,但它仍然是合理的。我们只是用来在断言assertWithMessage()
中包含有关列表的其他上下文:isInstanceOf()
assertThat(ls).hasSize(n);
for (int i = 0; i < ls.size(); i++) {
assertWithMessage("list: %s - index: %s", ls, i)
.that(ls.get(i)).isInstanceOf(String.class);
}
这比实现您自己的 custom 更具可读性和更清晰的正确性Subject
。
从Truth 0.29开始,您可以使用“ Fuzzy Truth ”AKA做得更好Correspondence
。这允许您从本质上描述集合的一些转换,然后断言该转换的结果。在这种情况下,我们将创建一个INSTANCEOF_CORRESPONDENCE
:
private static final Correspondence<Object, Class<?>> INSTANCEOF_CORRESPONDENCE =
new Correspondence<Object, Class<?>>() {
@Override
public boolean compare(@Nullable Object actual, @Nullable Class<?> expected) {
return expected.isInstance(actual);
}
@Override
public String toString() {
return "is instanceof";
}
};
现在你可以写一个漂亮的单行了!
assertThat(ls).comparingElementsUsing(INSTANCEOF_CORRESPONDENCE)
.containsExactly(String.class);
这种方法相对于自定义主题的最大好处是它更具可扩展性 - 如果您决定做出不同的断言,则Correspondence
实现不需要更改,只需您的断言:
assertThat(ls).comparingElementsUsing(INSTANCEOF_CORRESPONDENCE)
.doesNotContain(Integer.class);
还有一些暂定计划来支持方法引用和 lambda,.comparingElementsUsing()
这样您就可以编写如下内容:
assertThat(ls).comparingElementsUsing((o, c) -> c.isInstance(o), "is instanceof")
.containsExactly(String.class);