Mat*_*mes 2593 java tdd unit-testing
如何对具有内部私有方法,字段或嵌套类的类进行单元测试(使用xUnit)?或者通过内部链接(static
在C/C++中)或在私有(匿名)命名空间中使其成为私有的函数?
为了能够运行测试而更改方法或函数的访问修饰符似乎很糟糕.
Cem*_*kas 1572
如果您有一些遗留Java应用程序,并且不允许更改方法的可见性,那么测试私有方法的最佳方法是使用反射.
在内部,我们使用帮助器来获取/设置@Jailbreak
和private
变量以及调用private static
和private
方法.以下模式将允许您执行与私有方法和字段相关的任何操作.当然,你private static
不能通过反射来改变变量.
@Jailbreak Foo foo = new Foo();
// Direct, *type-safe* access to *all* foo's members
foo.privateMethod(x, y, z);
foo.privateField = value;
Run Code Online (Sandbox Code Playgroud)
对于领域:
Method method = TargetClass.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
return method.invoke(targetObject, argObjects);
Run Code Online (Sandbox Code Playgroud)
注意:
1.private static final
让你看看TargetClass.getDeclaredMethod(methodName, argClasses)
方法.同样的事情也适用private
.
2.getDeclaredField
需要与私人一起玩耍.
Tru*_*mpi 582
测试私有方法的最佳方法是通过另一种公共方法.如果无法执行此操作,则满足下列条件之一:
Jay*_*uzi 302
当我在一个足够复杂的类中使用私有方法时,我觉得需要直接测试私有方法,这就是代码味道:我的类太复杂了.
我解决此类问题的常用方法是梳理出一个包含有趣内容的新类.通常,此方法及其与之交互的字段以及可能的另一种方法可以被提取到新类中.
新类将这些方法公开为"公共",因此它们可以进行单元测试.新旧课程现在都比原来的课程简单,这对我来说很棒(我需要保持简单,否则我会迷路!).
请注意,我并不是说人们在不使用大脑的情况下创建课程!这里的要点是使用单元测试的力量来帮助您找到好的新类.
Jon*_*Jon 270
我过去曾使用反射来为Java做这个,在我看来这是一个很大的错误.
严格来说,您不应该编写直接测试私有方法的单元测试.您应该测试的是该类与其他对象的公共合同; 你永远不应该直接测试对象的内部.如果另一个开发人员想要对该类进行少量内部更改(这不会影响类公共合同),则他/她必须修改基于反射的测试以确保其有效.如果在整个项目中反复执行此操作,则单元测试将不再是对代码运行状况的有用度量,并开始成为开发的障碍,并对开发团队造成烦扰.
我建议改为使用Cobertura等代码覆盖工具,以确保您编写的单元测试在私有方法中提供良好的代码覆盖率.这样,您可以间接测试私有方法正在做什么,并保持更高的敏捷性.
Ike*_*nez 204
从本文:使用JUnit和SuiteRunner(Bill Venners)测试私有方法,您基本上有4个选项:
- 不要测试私有方法.
- 给方法包访问权限.
- 使用嵌套测试类.
- 使用反射.
Joh*_*ing 120
通常,单元测试旨在运用类或单元的公共接口.因此,私有方法是您不希望显式测试的实现细节.
Ric*_*ier 64
只是我想要测试私有方法的两个例子:
SecurityManager
没有配置来防止这种情况时).我理解只测试"合同"的想法.但是我没有看到人们可以提倡实际上没有测试代码 - 你的里程可能会有所不同.
因此,我的权衡涉及使JUnits与反射复杂化,而不是损害我的安全性和SDK.
sup*_*ova 37
我使用的另一种方法是将私有方法更改为包私有或受保护,然后使用Google Guava库的@VisibleForTesting注释对其进行补充.
这将告诉使用此方法的任何人要小心,即使在包中也不能直接访问它.另外,测试类不需要在物理上相同的包中,而是在测试文件夹下的相同包中.
例如,如果要测试的方法是,src/main/java/mypackage/MyClass.java
那么应该放入测试调用src/test/java/mypackage/MyClassTest.java
.这样,您就可以访问测试类中的测试方法.
pha*_*eim 33
为了测试大和古怪类遗留代码,它往往是能够测试一个私人(或公共)方法我正在写非常有帮助的现在.
我使用junitx.util.PrivateAccessor -package for Java.许多有用的单行程序用于访问私有方法和私有字段.
import junitx.util.PrivateAccessor;
PrivateAccessor.setField(myObjectReference, "myCrucialButHardToReachPrivateField", myNewValue);
PrivateAccessor.invoke(myObjectReference, "privateMethodName", java.lang.Class[] parameterTypes, java.lang.Object[] args);
Run Code Online (Sandbox Code Playgroud)
Gru*_*eck 27
在尝试了使用 Java 反射的 Cem Catikkas 解决方案之后,我不得不说他的解决方案比我在这里描述的更优雅.但是,如果您正在寻找使用反射的替代方法,并且可以访问您正在测试的源,那么这仍然是一个选项.
测试类的私有方法可能是有用的,特别是在测试驱动开发中,您希望在编写任何代码之前设计小型测试.
创建可访问私有成员和方法的测试可以测试难以专门定位的代码区域,只能访问公共方法.如果公共方法涉及多个步骤,则它可以包含多个私有方法,然后可以单独进行测试.
好处:
缺点:
但是,如果连续测试需要这种方法,则可能是应该提取私有方法的信号,可以以传统的公共方式进行测试.
这是一个如何工作的复杂的例子:
// Import statements and package declarations
public class ClassToTest
{
private int decrement(int toDecrement) {
toDecrement--;
return toDecrement;
}
// Constructor and the rest of the class
public static class StaticInnerTest extends TestCase
{
public StaticInnerTest(){
super();
}
public void testDecrement(){
int number = 10;
ClassToTest toTest= new ClassToTest();
int decremented = toTest.decrement(number);
assertEquals(9, decremented);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(StaticInnerTest.class);
}
}
}
Run Code Online (Sandbox Code Playgroud)
内部类将被编译为ClassToTest$StaticInnerTest
.
Mik*_*ail 26
在Spring Framework中,您可以使用此方法测试私有方法:
ReflectionTestUtils.invokeMethod()
Run Code Online (Sandbox Code Playgroud)
例如:
ReflectionTestUtils.invokeMethod(TestClazz, "createTest", "input data");
Run Code Online (Sandbox Code Playgroud)
Ant*_*oth 24
私有方法由公共方法使用.否则,它们就是死代码.这就是为什么你测试公共方法,断言公共方法的预期结果,从而断言它消耗的私有方法.
在对公共方法运行单元测试之前,应通过调试来测试私有方法的测试.
它们也可以使用测试驱动开发进行调试,调试单元测试直到满足所有断言.
我个人认为使用TDD创建类更好; 创建公共方法存根,然后使用预先定义的所有断言生成单元测试,因此在编码之前确定方法的预期结果.这样,您就不会走错路径,使单元测试断言符合结果.您的课程非常强大,并且在所有单元测试通过后都符合要求.
Ste*_*ers 23
如果使用Spring,ReflectionTestUtils提供了一些方便的工具,只需要很少的工作就可以帮助你.例如,要在私有成员上设置模拟而不必强制添加不需要的公共setter:
ReflectionTestUtils.setField(theClass, "theUnsettableField", theMockObject);
Run Code Online (Sandbox Code Playgroud)
Don*_*kby 22
如果您正在尝试测试您不愿意或无法更改的现有代码,那么反射是一个不错的选择.
如果类的设计仍然是灵活的,并且你有一个复杂的私有方法,你想单独测试,我建议你把它拉出一个单独的类并分别测试该类.这不必更改原始类的公共接口; 它可以在内部创建辅助类的实例并调用辅助方法.
如果您想测试来自辅助方法的困难错误条件,您可以更进一步.从helper类中提取一个接口,将一个公共getter和setter添加到原始类中以注入帮助器类(通过其接口使用),然后将一个helper类的模拟版本注入到原始类中以测试原始类的方式响应助手的异常.如果您想在不测试帮助程序类的情况下测试原始类,这种方法也很有用.
ema*_*ema 17
测试私有方法会破坏类的封装,因为每次更改内部实现时都会破坏客户端代码(在本例中为测试).
所以不要测试私有方法.
sim*_*ico 16
JUnit.org FAQ页面的答案:
但如果你必须......
如果您使用的是JDK 1.3或更高版本,则可以使用反射在PrivilegedAccessor的帮助下破坏访问控制机制.有关如何使用它的详细信息,请阅读本文.
如果您使用的是JDK 1.6或更高版本并使用@Test注释测试,则可以使用Dp4j在测试方法中注入反射.有关如何使用它的详细信息,请参阅此测试脚本.
GRO*_*X13 13
我建议你稍微重构一下你的代码.当你不得不开始考虑使用反射或其他类型的东西时,为了测试你的代码,你的代码就会出现问题.
你提到了不同类型的问题.让我们从私人领域开始.在私有字段的情况下,我会添加一个新的构造函数并将注入的字段注入其中.而不是这个:
public class ClassToTest {
private final String first = "first";
private final List<String> second = new ArrayList<>();
...
}
Run Code Online (Sandbox Code Playgroud)
我用过这个:
public class ClassToTest {
private final String first;
private final List<String> second;
public ClassToTest() {
this("first", new ArrayList<>());
}
public ClassToTest(final String first, final List<String> second) {
this.first = first;
this.second = second;
}
...
}
Run Code Online (Sandbox Code Playgroud)
即使使用一些遗留代码,这也不会成为问题.旧代码将使用空构造函数,如果您问我,重构代码看起来更干净,并且您将能够在没有反射的情况下在测试中注入必要的值.
现在关于私人方法.根据我个人的经验,当您必须存根私有方法进行测试时,该方法与该类无关.在这种情况下,一个常见的模式是将它包装在一个接口中,Callable
然后你也在构造函数中传入该接口(使用那个多个构造函数技巧):
public ClassToTest() {
this(...);
}
public ClassToTest(final Callable<T> privateMethodLogic) {
this.privateMethodLogic = privateMethodLogic;
}
Run Code Online (Sandbox Code Playgroud)
我写的大部分内容看起来都是依赖注入模式.根据我的个人经验,它在测试时非常有用,我认为这种代码更清晰,更易于维护.关于嵌套类,我会说同样的.如果嵌套类包含重逻辑,那么如果将它作为包私有类移动并将其注入需要它的类中会更好.
我在重构和维护遗留代码时也使用了其他几种设计模式,但这完全取决于要测试的代码的情况.使用反射主要不是问题,但是当你有一个经过严格测试的企业应用程序并且在每次部署之前运行测试时,一切都变得非常慢(这很烦人,而且我不喜欢那种东西).
还有二次注射,但我不建议使用它.我最好坚持使用构造函数并在必要时初始化所有内容,留下注入必要依赖项的可能性.
Yul*_*iri 11
这是我测试私有字段的通用函数:
protected <F> F getPrivateField(String fieldName, Object obj)
throws NoSuchFieldException, IllegalAccessException {
Field field =
obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return (F)field.get(obj);
}
Run Code Online (Sandbox Code Playgroud)
Sni*_*las 10
今天,我推出了一个Java库来帮助测试私有方法和字段.它在设计时考虑到了Android,但它确实可以用于任何Java项目.
如果您获得了一些私有方法或字段或构造函数的代码,则可以使用BoundBox.它完全符合您的要求.下面是一个测试示例,该测试访问Android活动的两个私有字段以对其进行测试:
@UiThreadTest
public void testCompute() {
// Given
boundBoxOfMainActivity = new BoundBoxOfMainActivity(getActivity());
// When
boundBoxOfMainActivity.boundBox_getButtonMain().performClick();
// Then
assertEquals("42", boundBoxOfMainActivity.boundBox_getTextViewMain().getText());
}
Run Code Online (Sandbox Code Playgroud)
BoundBox可以轻松测试私有/受保护的字段,方法和构造函数.您甚至可以访问通过继承隐藏的内容.实际上,BoundBox打破了封装.它将允许您通过反射访问所有这些,但是在编译时检查所有内容.
它是测试一些遗留代码的理想选择.仔细使用它.;)
https://github.com/stephanenicolas/boundbox
小智 10
请看下面的例子;
应添加以下import语句:
import org.powermock.reflect.Whitebox;
Run Code Online (Sandbox Code Playgroud)
现在,您可以直接传递具有私有方法,要调用的方法名称以及其他参数的对象,如下所示.
Whitebox.invokeMethod(obj, "privateMethod", "param1");
Run Code Online (Sandbox Code Playgroud)
我想分享一条关于测试的规则,该规则特别与该主题相关:
\n\n\n我认为您永远不应该为了更轻松地编写测试而调整生产代码。
\n
其他帖子中有一些建议说您应该调整原始类以测试私有方法 - 请首先红色此警告。
\n如果我们将方法/字段的可访问性更改为私有或受保护,只是为了使其可供测试访问,那么我们就违背了私有访问指令存在的目的。
\n当我们想要进行测试驱动开发时,为什么我们应该拥有私有字段/方法/类时,为什么我们应该拥有私有字段/方法/类?我们是否应该将所有内容声明为包私有,甚至公开,以便我们可以毫不费力地进行测试?\xe2\x80\x94我不这么认为。
\n从另一个角度来看:测试不应该给生产应用程序的性能和执行带来负担。
\n如果我们只是为了更容易测试而更改生产代码,则可能会以某种方式加重性能和应用程序的执行。
\n如果有人开始将私有访问更改为包私有,那么开发人员最终可能会想出其他“巧妙的想法”,向原始类添加更多代码。这会给可读性带来额外的干扰,并可能降低应用程序的性能。
\n通过将私有访问更改为限制较少的访问,我们为开发人员在应用程序的未来开发中滥用新情况提供了可能性。我们不是强迫他/她以正确的方式发展,而是用新的可能性来诱惑他/她,并赋予他在未来做出错误选择的能力。
\n当然,这个规则可能会有一些例外,但是清楚地了解后,什么是规则,什么是例外?我们需要绝对确定我们知道为什么会引入这种异常。
\n首先,我会抛出这个问题:为什么你的私人成员需要隔离测试?它们是否复杂,提供如此复杂的行为以至于需要在公共场所进行测试?它是单元测试,而不是"代码行"测试.不要出汗小东西.
如果他们那么大,足够大,以至于这些私人成员都是一个复杂性很大的"单位" - 考虑重构这些私人成员.
如果重构不合适或不可行,您可以使用策略模式在单元测试下替换对这些私有成员函数/成员类的访问吗?在单元测试下,该策略将提供额外的验证,但在发布版本中,它将是简单的直通.
我最近遇到了这个问题并编写了一个名为Picklock的小工具,它避免了显式使用Java反射API的问题,两个例子:
调用方法,例如private void method(String s)
- 通过Java反射
Method method = targetClass.getDeclaredMethod("method", String.class);
method.setAccessible(true);
return method.invoke(targetObject, "mystring");
Run Code Online (Sandbox Code Playgroud)
调用方法,例如private void method(String s)
- 由Picklock
interface Accessible {
void method(String s);
}
...
Accessible a = ObjectAccess.unlock(targetObject).features(Accessible.class);
a.method("mystring");
Run Code Online (Sandbox Code Playgroud)
设置字段,例如private BigInteger amount;
- 通过Java反射
Field field = targetClass.getDeclaredField("amount");
field.setAccessible(true);
field.set(object, BigInteger.valueOf(42));
Run Code Online (Sandbox Code Playgroud)
设置字段,例如private BigInteger amount;
- 由Picklock
interface Accessible {
void setAmount(BigInteger amount);
}
...
Accessible a = ObjectAccess.unlock(targetObject).features(Accessible.class);
a.setAmount(BigInteger.valueOf(42));
Run Code Online (Sandbox Code Playgroud)
对于Java我会使用反射,因为我不喜欢只是为了测试而在声明的方法上更改对包的访问的想法.但是,我通常只测试公共方法,这也应该确保私有方法正常工作.
您不能使用反射从所有者类外部获取私有方法,私有修饰符也会影响反射
这不是真的.你肯定可以,正如Cem Catikkas的回答中提到的那样.
在c ++中:在包含具有要对其进行测试的私有函数的类头之前
使用此代码:
#define private public
#define protected public
Run Code Online (Sandbox Code Playgroud)
PowerMockito就是为此而制造的.使用maven依赖
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-mockito-release-full</artifactId>
<version>1.6.4</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)
那你可以做
import org.powermock.reflect.Whitebox;
...
MyClass sut = new MyClass();
SomeType rval = Whitebox.invokeMethod(sut, "myPrivateMethod", params, moreParams);
Run Code Online (Sandbox Code Playgroud)
小智 6
我认为大多数答案都过于粗糙。是否应该测试私有方法取决于您的代码。
我过去使用私有方法测试,并且是通过反射来完成的。这个对我有用。我意识到它的问题,但对我来说这是最好的解决方案。
我有一个繁重的应用程序,可以模拟超过一百万人的人类行为。每个人都由一个物体代表。该应用程序的主要目的是跟踪某些事物(疾病、信息、思想)如何在人群中传播。
为此,我有将疾病或信息从一个对象传递到另一个对象的方法。绝对没有理由公开这些方法,因为最终用户对人与人之间的一次传递不感兴趣。最终用户只对其如何在人群中传播的总体情况感兴趣。所以这些方法是私有的。
但我想确切地知道将一点信息从一个人传递给另一个人的单一行为是否在做它应该做的事情。通过公共用户界面对其进行测试也是不可能的,因为它不是公开的,而且我认为仅仅为了测试的目的而将其公开是很尴尬的。应用程序的最终输出是由始终执行的数亿个此类单个步骤定义的。我也无法测试最终输出,因为这涉及复杂的随机性,这使得测试变得难以预测。
因此,我测试应用程序的方法是测试将信息从一个人传递给另一个人的单个步骤。这些都是私有方法。所以我使用反射来测试它。
我讲这个故事是为了表明这不是一个简单的黑白故事。这取决于您的应用程序,什么是最好的。在某些情况下,通过反射测试私有方法可能是您的最佳选择。
如果这里的一些人在我的用例中知道更好的解决方案,我当然很乐意纠正......
您可以关闭 Java 反射的访问限制,这样私有就没有任何意义。
该setAccessible(true)
调用做到这一点。
唯一的限制是 ClassLoader 可能不允许您这样做。
有关在 Java 中执行此操作的方法,请参阅Subverting Java Access Protection for Unit Testing (Ross Burton)。
Android 有@VisibleForTesting
来自android.support.annotation
包的注释。
该
@VisibleForTesting
注释表明带注释的方法比通常使该方法可测试所需的更加可见。此注释有一个可选otherwise
参数,可让您指定方法的可见性(如果不需要使其对测试可见)。Lint 使用otherwise
参数来强制实现预期的可见性。
实际上,这意味着您应该打开一个方法进行测试,并且@VisibleForTesting
注释将显示警告。
例如
package com.mypackage;
public class ClassA {
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
static void myMethod() {
}
}
Run Code Online (Sandbox Code Playgroud)
当您在同一包(com.mypackage)中调用 ClassA.myMethod() 时,您将看到警告。
归档时间: |
|
查看次数: |
819316 次 |
最近记录: |