公开课A { 公共无效方法(布尔b){ 如果(b == true) method1(); 其他 method2(); } 私有void method1(){} 私有void method2(){} }
公共课程TestA { @测试 公共无效testMethod(){ 一个a =模拟(A.class); a.method(true); //如何像verify(a).method1();一样进行测试; } }
如何调用私有方法,以及如何使用Mockito测试私有方法???
公开课A { 公共无效方法(布尔b){ 如果(b == true) method1(); 其他 method2(); } 私有void method1(){} 私有void method2(){} }
公共课程TestA { @测试 公共无效testMethod(){ 一个a =模拟(A.class); a.method(true); //如何像verify(a).method1();一样进行测试; } }
如何调用私有方法,以及如何使用Mockito测试私有方法???
Answers:
无法通过模仿。从他们的维基
为什么Mockito不模拟私有方法?
首先,我们并不是在嘲笑私有方法。我们只是不关心私有方法,因为从测试私有方法的角度来看不存在。Mockito不模拟私有方法的原因有两个:
它需要对类加载器进行黑客攻击,而这些子加载器从来都不是防弹的,它会更改api(您必须使用自定义测试运行器,对类进行注释等)。
解决起来非常容易-只需将方法的可见性从私有更改为受包保护(或受保护)即可。
这需要我花时间实施和维护它。考虑到第二点,并且已经在不同的工具(powermock)中实现了,这是没有意义的。
最后...模拟私有方法暗示对OO理解有问题。在OO中,您希望对象(或角色)进行协作,而不是方法。忘记Pascal和程序代码。思考对象。
这是一个如何使用powermock进行操作的小示例
public class Hello {
private Hello obj;
private Integer method1(Long id) {
return id + 10;
}
}
要测试method1,请使用以下代码:
Hello testObj = new Hello();
Integer result = Whitebox.invokeMethod(testObj, "method1", new Long(10L));
要设置私有对象obj,请使用以下命令:
Hello testObj = new Hello();
Hello newObject = new Hello();
Whitebox.setInternalState(testObj, "obj", newObject);
从行为的角度考虑此问题,而不是从现有的方法角度考虑。method
如果b
为true,则调用的方法具有特定的行为。如果b
为假,则具有不同的行为。这意味着您应该为编写两个不同的测试method
;每种情况一个。因此,您不必进行三个面向方法的测试(一个用于method
,一个用于method1
,一个用于method2
,用于测试,而是进行两个面向行为的测试。
与此相关(我最近在另一个SO线程中提出了这个建议,结果被称为四个字母的单词,因此请随便带一点盐);我发现选择反映我正在测试的行为的测试名称而不是方法的名称很有帮助。所以,不要打电话给你测试testMethod()
,testMethod1()
,testMethod2()
等等。我喜欢类似calculatedPriceIsBasePricePlusTax()
或taxIsExcludedWhenExcludeIsTrue()
表示我正在测试的行为的名称;然后在每种测试方法中,仅测试指定的行为。大多数此类行为将只涉及对公共方法的一次调用,但可能涉及对私有方法的许多调用。
希望这可以帮助。
尽管Mockito不提供该功能,但您可以使用Mockito + JUnit ReflectionUtils类或Spring ReflectionTestUtils类来获得相同的结果。请参见下面的示例,该示例从此处获取解释如何调用私有方法的示例:
ReflectionTestUtils.invokeMethod(student, "saveOrUpdate", "From Unit test");
可以在《Mockito for Spring》一书中找到ReflectionTestUtils和Mockito的完整示例。
您不应该测试私有方法。只需要测试非私有方法,因为无论如何它们都应该调用私有方法。如果您“想”测试私有方法,则可能表明您需要重新考虑您的设计:
我在使用适当的依赖注入吗?我是否可能需要将私有方法移到单独的类中并进行测试?这些方法必须私有吗?...它们不是默认值还是受保护的?
在上面的实例中,实际上可能需要将被称为“随机”的两个方法放置在它们自己的类中,进行测试,然后将其注入上述类中。
我能够使用反射使用嘲笑来测试内部的私有方法。这是示例,尝试命名为有意义
//Service containing the mock method is injected with mockObjects
@InjectMocks
private ServiceContainingPrivateMethod serviceContainingPrivateMethod;
//Using reflection to change accessibility of the private method
Class<?>[] params = new Class<?>[]{PrivateMethodParameterOne.class, PrivateMethodParameterTwo.class};
Method m = serviceContainingPrivateMethod .getClass().getDeclaredMethod("privateMethod", params);
//making private method accessible
m.setAccessible(true);
assertNotNull(m.invoke(serviceContainingPrivateMethod, privateMethodParameterOne, privateMethodParameterTwo).equals(null));
通过使用反射,可以从测试类中调用私有方法。在这种情况下,
// test方法将像这样...
public class TestA {
@Test
public void testMethod() {
A a= new A();
Method privateMethod = A.class.getDeclaredMethod("method1", null);
privateMethod.setAccessible(true);
// invoke the private method for test
privateMethod.invoke(A, null);
}
}
如果私有方法调用任何其他私有方法,那么我们需要监视对象并存根另一个方法。测试类将类似于...
// test方法将像这样...
public class TestA {
@Test
public void testMethod() {
A a= new A();
A spyA = spy(a);
Method privateMethod = A.class.getDeclaredMethod("method1", null);
privateMethod.setAccessible(true);
doReturn("Test").when(spyA, "method2"); // if private method2 is returning string data
// invoke the private method for test
privateMethod.invoke(spyA , null);
}
}
**方法是结合反射和监视对象。method1和method2是私有方法,method1调用method2。
我不太了解您是否需要测试私有方法。根本问题是您的公共方法的返回类型为void,因此您无法测试您的公共方法。因此,您不得不测试您的私有方法。我的猜测正确吗?
一些可能的解决方案(AFAIK):
模拟您的私有方法,但仍然不会“实际”测试您的方法。
验证方法中使用的对象的状态。大多数方法要么对输入值进行一些处理并返回输出,要么更改对象的状态。也可以采用测试对象的期望状态。
public class A{ SomeClass classObj = null; public void publicMethod(){ privateMethod(); } private void privateMethod(){ classObj = new SomeClass(); } }
[在这里,您可以通过检查classObj从null到not null的状态变化来测试私有方法。
稍微重构代码(希望这不是遗留代码)。我写方法的基本原理是,应该总是返回一些内容(一个int /布尔值)。返回值可以或不可以被实现使用,但一定会被测试使用
码。
public class A
{
public int method(boolean b)
{
int nReturn = 0;
if (b == true)
nReturn = method1();
else
nReturn = method2();
}
private int method1() {}
private int method2() {}
}
实际上,有一种方法可以通过Mockito从私有成员测试方法。假设您有一个这样的课程:
public class A {
private SomeOtherClass someOtherClass;
A() {
someOtherClass = new SomeOtherClass();
}
public void method(boolean b){
if (b == true)
someOtherClass.method1();
else
someOtherClass.method2();
}
}
public class SomeOtherClass {
public void method1() {}
public void method2() {}
}
如果要测试a.method
将从中调用方法SomeOtherClass
,则可以编写如下内容。
@Test
public void testPrivateMemberMethodCalled() {
A a = new A();
SomeOtherClass someOtherClass = Mockito.spy(new SomeOtherClass());
ReflectionTestUtils.setField( a, "someOtherClass", someOtherClass);
a.method( true );
Mockito.verify( someOtherClass, Mockito.times( 1 ) ).method1();
}
ReflectionTestUtils.setField();
会将您可以监视的内容存根给私人成员。
将您的测试放在相同的程序包中,但放在不同的源文件夹(src / main / java与src / test / java)中,并使这些方法成为程序包专用的。Imo可测试性比隐私更重要。
如果private方法不是void并且返回值用作外部依赖项的方法的参数,则可以模拟该依赖项并使用an ArgumentCaptor
来捕获返回值。例如:
ArgumentCaptor<ByteArrayOutputStream> csvOutputCaptor = ArgumentCaptor.forClass(ByteArrayOutputStream.class);
//Do your thing..
verify(this.awsService).uploadFile(csvOutputCaptor.capture());
....
assertEquals(csvOutputCaptor.getValue().toString(), "blabla");