public class A { public void method(boolean b){ if (b == true) method1(); else method2(); } private void method1() {} private void method2() {} }
public class TestA { @Test public void testMethod() { A a = mock(A.class); a.method(true); //how to test like verify(a).method1(); } }
如何测试私有方法是否被调用,以及如何使用mockito测试私有方法???
答案 0 :(得分:115)
通过mockito不可能。来自他们的wiki
为什么Mockito不会模仿私有方法?
首先,我们对于嘲弄私人方法并不是教条主义。我们刚刚 不要关心私人方法,因为从这个角度来看 测试私有方法不存在。这有几个原因 Mockito不会模仿私人方法:
它需要对类加载器进行黑客攻击,而这种类加载器绝不是防弹的 更改api(您必须使用自定义测试运行器,注释该类, 等等。)。
这很容易解决 - 只需更改方法的可见性 从私有到包受保护(或受保护)。
这要求我花时间实施&保持它。它 在第2点和事实已经存在的情况下没有意义 在不同的工具(powermock)中实现。
最后......嘲弄私有方法暗示有一些东西 OO理解错误。在OO中,您需要对象(或角色) 合作,而不是方法。忘记pascal&程序代码。认为 在对象中。
答案 1 :(得分:68)
答案 2 :(得分:26)
以下是如何使用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);
答案 3 :(得分:15)
从行为的角度考虑这一点,而不是根据哪些方法。如果method
为真,则称为b
的方法具有特定行为。如果b
为false,则会有不同的行为。这意味着您应该为method
编写两个不同的测试;每个案例一个。因此,不是有三个面向方法的测试(一个用于method
,一个用于method1
,一个用于method2
,而是有两个面向行为的测试。
与此相关(我最近在另一个SO线程中提出了这个问题,并且结果被称为四个字母的单词,所以请随意拿出一些盐);我发现选择反映我正在测试的行为的测试名称是有帮助的,而不是方法的名称。因此,请勿调用您的测试testMethod()
,testMethod1()
,testMethod2()
等等。我喜欢像calculatedPriceIsBasePricePlusTax()
或taxIsExcludedWhenExcludeIsTrue()
这样的名字来表明我正在测试的行为;然后在每个测试方法中,仅测试指示的行为。大多数此类行为只涉及对公共方法的一次调用,但可能涉及对私有方法的多次调用。
希望这有帮助。
答案 4 :(得分:7)
通过使用反射,可以从测试类中调用私有方法。 在这种情况下,
// 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);
}
}
**方法是将反射和监视对象结合起来。 方法1和方法2是私有方法,方法1调用方法2。
答案 5 :(得分:6)
您不应该测试私有方法。只需要测试非私有方法,因为这些方法应该调用私有方法。如果您“想要”测试私有方法,则可能表明您需要重新考虑您的设计:
我使用适当的依赖注入吗? 我是否可能需要将私有方法移动到单独的类中而是测试它? 这些方法必须是私有的吗? ......他们不能默认或受保护吗?
在上面的例子中,两个被称为“随机”的方法实际上可能需要放在自己的类中,经过测试然后注入上面的类。
答案 6 :(得分:5)
我能够使用反射使用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));
答案 7 :(得分:3)
尽管Mockito不提供该功能,但您可以使用Mockito + JUnit ReflectionUtils类或Spring ReflectionTestUtils类来获得相同的结果。请查看下面摘自here的示例,说明如何调用私有方法:
ReflectionTestUtils.invokeMethod(student, "saveOrUpdate", "From Unit test");
在Mockito for Spring这本书中可以找到带有ReflectionTestUtils和Mockito的完整示例。
答案 8 :(得分:2)
我真的不明白你需要测试私有方法。根本问题是您的公共方法作为返回类型具有void,因此您无法测试您的公共方法。因此,您被迫测试您的私人方法。我的猜测是否正确?
一些可能的解决方案(AFAIK):
嘲笑你的私人方法,但你仍然不会“实际”测试你的方法。
验证方法中使用的对象的状态。 MOSTLY方法要么对输入值进行一些处理并返回输出,要么改变对象的状态。也可以使用测试对象的所需状态。
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() {}
}
答案 9 :(得分:1)
将测试放在同一个包中,但是使用不同的源文件夹(src / main / java与src / test / java)并将这些方法设置为package-private。 Imo可测试性比隐私更重要。
答案 10 :(得分:0)
实际上,有一种方法可以通过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();
会将您可以监视的内容存入私有成员。
答案 11 :(得分:0)
如果private方法不是void并且将返回值用作外部依赖项的方法的参数,则可以模拟该依赖项并使用ArgumentCaptor
来捕获返回值。
例如:
ArgumentCaptor<ByteArrayOutputStream> csvOutputCaptor = ArgumentCaptor.forClass(ByteArrayOutputStream.class);
//Do your thing..
verify(this.awsService).uploadFile(csvOutputCaptor.capture());
....
assertEquals(csvOutputCaptor.getValue().toString(), "blabla");