如何在纯JUnit测试中使用VisibleForTesting?

58
我正在对我的纯Java项目中的纯JUnit4 Java文件运行测试,但我找不到一种清晰地使用@VisibleForTesting的方法,而不将其手动设置为公共内容。
@VisibleForTesting
public Address getAddress() {
  return mAddress;
}

这个方法必须是public,以允许它在测试中被“公开”调用,但在这种情况下,注释就没有意义了,对吧?如果该注释不起作用,为什么不只使用注释呢?

4个回答

66

将该方法的访问权限设为包级私有,那么测试代码就能够访问它了,只要测试代码和生产代码在同一包内。

@VisibleForTesting
Address getAddress() {
  return mAddress;
}

考虑重构您的代码,以便您不需要显式测试私有方法,尝试测试公共接口的行为。难以测试的代码可能表明生产代码可以进行改进。

注释的目的是作为惯例,并可用于静态代码分析,而注释不能。


不确定为什么这个回答有那么多赞。它与被接受的答案相同,只是灵活性要少得多。被接受的答案不需要您将生产代码放入与单元测试相同的包中。 - mpellegr
6
请注意,私有方法是不可见或难以直接测试的,建议尝试重构您的代码,以便您不需要显式地测试私有方法,而是尝试测试公共接口的行为。但这并非绝对,有时您需要通过将一组参数传递到某个类A中来进行依赖项注入,以创建类B的对象。我不同意原文的观点。我曾经为类A创建了一个公共构造函数,以便它可以直接使用类B,而不是使用类B的getInstance()方法。我采纳了其他答案中提供的解决方案,因为我不能将我的单元测试移到不同的包中来使它正常工作。这对我来说似乎是一个解决方法,而被采纳的答案更好。 - mpellegr
@MikeJ,请问你可以详细说明一下以下评论:“还应考虑重构代码,以便无需显式测试私有方法。”一个方法/函数应该只负责一项任务。如果它了解给定类的实现,则可能是私有的,但是却可以非常有用地根据各种输入进行测试。 - Andy Marchewka
如果编写测试以测试行为而不是实现,则可以通过将某些内容拆分为私有方法来重构公共方法,并且现有的测试仍将覆盖私有方法。TDD非常适合此类情况,因为测试是在实现之前完成的。关键是测试行为,涵盖类下公共可访问API的所有输入。在实现私有方法后编写测试要困难得多。通过测试行为,您可以重构生产代码,而无需更改测试代码。 - MikeJ

37
根据Android文档

如果没有测试,您可以选择指定可见性本来应该是什么; 这使工具可以捕获生产代码内的意外访问。

例如:

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
public Address getAddress()

12
@mpellegr,然而它确实提高了代码的整体可读性,特别是对于新手开发者来说。 - Gerrit Sedlaczek

34

标签本身有助于linter识别不需要的访问。

为了降低直接使用它的风险,在Kotlin中将这些方法添加为internal,在Java中将其添加为protected而不是public,这样只有在同一包中的测试或类才能访问该方法。

Java:

@VisibleForTesting
protected Address address() {
  return mAddress;
}

Kotlin:

@VisibleForTesting
internal fun address(): Address {
  return address;
}

1
从注释的JavaDoc中:“<b>不要在公共或受保护的声明中使用此接口</b>。”其他答案是正确的,在Java中,这些方法应该具有包可见性。 - winne2

8

@VisibleForTesting注解用于Guava中的包方法,并不是JUnit API的一部分。该注解只是一个标记,表示该方法可以被测试。它甚至不会在JVM中加载。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接