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测试私有方法?
不可能通过mockito。从他们的wiki
为什么 Mockito 不模拟私有方法?首先,我们对模拟私有方法并不教条。我们只是不关心私有方法,因为从测试私有方法的角度来看并不存在。以下是 Mockito 不模拟私有方法的几个原因:它需要对永远不会防弹的类加载器进行黑客攻击,并且它会更改 api(您必须使用自定义测试运行器、注释类等)。这很容易解决 - 只需将方法的可见性从私有更改为包保护(或受保护)。它需要我花时间实施和维护它。考虑到第 2 点以及它已经在不同的工具(powermock)中实现的事实,这没有任何意义。最后... Mocking private 方法是对OO理解有问题的暗示。在 OO 中,您希望对象(或角色)协作,而不是方法。忘记帕斯卡和程序代码。在对象中思考。
这是一个小示例,如何使用 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);
虽然 Mockito 不提供该功能,但您可以使用 Mockito + JUnit ReflectionUtils 类或 Spring ReflectionTestUtils 类获得相同的结果。请参阅以下 here 中的示例,说明如何调用私有方法:
ReflectionTestUtils.invokeMethod(student, "saveOrUpdate", "From Unit test");
使用 ReflectionTestUtils 和 Mockito 的完整示例可以在书 Mockito for Spring 中找到。
官方文档Spring Testing
通过使用反射,可以从测试类调用私有方法。在这种情况下, //test 方法将是这样的 ... public class TestA { @Test public void testMethod() { A a= new A();方法 privateMethod = A.class.getDeclaredMethod("method1", null); privateMethod.setAccessible(true); // 调用私有方法进行测试 privateMethod.invoke(A, null);如果私有方法调用任何其他私有方法,那么我们需要监视对象并存根另一个方法。测试类将像 ... //测试方法将像这样 ... public class TestA { @测试 public void testMethod() { A a= new A();间谍A = spy(a);方法 privateMethod = A.class.getDeclaredMethod("method1", null); privateMethod.setAccessible(true); doReturn("Test").when(spyA, "method2"); // 如果私有方法 2 返回字符串数据 // 调用私有方法进行测试 privateMethod.invoke(spyA , null); } }
**方法是结合反射和窥探对象。 **method1 和 **method2 是私有方法,method1 调用method2。
从行为的角度考虑这一点,而不是从有什么方法的角度考虑。如果 b
为真,则名为 method
的方法具有特定的行为。如果 b
为假,它有不同的行为。这意味着您应该为 method
编写两个不同的测试;每个案例一个。因此,您不必拥有三个面向方法的测试(一个用于 method
、一个用于 method1
、一个用于 method2
),而是有两个面向行为的测试。
与此相关(我最近在另一个 SO 线程中提出了这个建议,结果被称为一个四个字母的单词,所以请随意接受这个);我发现选择反映我正在测试的行为的测试名称而不是方法的名称很有帮助。因此,不要将您的测试称为 testMethod()
、testMethod1()
、testMethod2()
等。我喜欢 calculatedPriceIsBasePricePlusTax()
或 taxIsExcludedWhenExcludeIsTrue()
之类的名称,这些名称表明我正在测试什么行为;然后在每种测试方法中,仅测试指示的行为。大多数此类行为仅涉及对公共方法的一次调用,但可能涉及对私有方法的多次调用。
希望这可以帮助。
您不应该测试私有方法。只需要测试非私有方法,因为无论如何这些都应该调用私有方法。如果您“想要”测试私有方法,则可能表明您需要重新考虑您的设计:
我是否使用了正确的依赖注入?我是否可能需要将私有方法移动到一个单独的类中并进行测试?这些方法必须是私有的吗? ...他们不能默认或受保护吗?
在上面的例子中,这两个被“随机”调用的方法实际上可能需要放在它们自己的一个类中,测试然后注入到上面的类中。
我能够使用反射使用模拟测试内部的私有方法。这是示例,尝试将其命名为有意义
//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));
我真的不明白你需要测试私有方法。根本问题是您的公共方法具有 void 作为返回类型,因此您无法测试您的公共方法。因此,您被迫测试您的私有方法。我的猜测正确吗??
一些可能的解决方案(AFAIK):
模拟您的私有方法,但您仍然不会“实际”测试您的方法。验证方法中使用的对象的状态。大多数方法要么对输入值进行一些处理并返回输出,要么更改对象的状态。也可以使用测试对象的期望状态。公共类 A { SomeClass classObj = null;公共无效公共方法(){私人方法(); } private void privateMethod(){ classObj = new SomeClass(); } } [在这里,您可以通过检查 classObj 从 null 到非 null 的状态变化来测试私有方法。] 稍微重构您的代码(希望这不是遗留代码)。我编写方法的基础是,一个人应该总是返回一些东西(一个 int/一个布尔值)。返回值可能或可能不会被实现使用,但它肯定会被测试代码使用。公共类 A { 公共 int 方法(布尔 b) { int nReturn = 0; if (b == true) nReturn = method1();否则 nReturn = method2(); } 私有 int 方法 1() {} 私有 int 方法 2() {} }
实际上有一种方法可以使用 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 可测试性比隐私更重要。
在私有方法不是 void 并且返回值用作外部依赖项方法的参数的情况下,您可以模拟依赖项并使用 ArgumentCaptor
来捕获返回值。例如:
ArgumentCaptor<ByteArrayOutputStream> csvOutputCaptor = ArgumentCaptor.forClass(ByteArrayOutputStream.class);
//Do your thing..
verify(this.awsService).uploadFile(csvOutputCaptor.capture());
....
assertEquals(csvOutputCaptor.getValue().toString(), "blabla");
以@aravind-yarram 的回答为基础:不可能通过模拟。从他们的wiki
那么测试私有方法的OO方式是什么?具有复杂逻辑的私有方法可能表明您的类违反了单一职责原则,并且应该将一些逻辑移至新类。
实际上,通过将这些私有方法提取到更细化类的公共方法中,您可以对它们进行单元测试,而不会破坏原始类的封装。
不定期副业成功案例分享