Testing Private method using mockito

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();
}
}

How to test private method is called or not, and how to test private method using mockito?

515040 次浏览

将测试放在同一个包中,但是放在不同的源文件夹(src/main/java vs. src/test/java)中,并将这些方法放在私有的包中。Imo 可测试性比隐私更重要。

不可能通过模仿。从他们的 维基百科

为什么 Mockito 不嘲笑私有方法?

首先,我们并不固执地嘲笑私人方法,我们只是 不关心私有方法,因为从 测试私有方法不存在 Mockito 并不嘲笑私有方法:

它需要黑客的类加载器,从来没有子弹和它 更改 api (必须使用自定义测试运行程序,注释类, 等)。

这是非常容易的工作周围-只是改变方法的可见性 从私有到包保护(或保护)。

它需要我花时间去实现和维护它 没有意义给定点 # 2和一个事实,它已经是 在不同的工具(powermock)中实现。

最后... ... 嘲笑私有方法是一个暗示,表明存在一些问题 面向对象理解错误。在面向对象中,您希望对象(或角色) 协作,而不是方法。忘记帕斯卡和过程代码 在物体中。

对于 Mockito 不能这样做,但是可以使用 Powermock扩展 Mockito 和 Mock 私有方法。Powermock 支持 Mockito。给你就是个例子。

您不应该测试私有方法。只有非私有方法需要进行测试,因为这些方法无论如何都应该调用私有方法。如果您“想”测试私有方法,这可能表明您需要重新考虑您的设计:

我用的依赖注入合适吗? 我是否有可能需要将私有方法移动到一个单独的类中并对其进行测试? 这些方法必须是私有的吗? ... 难道它们不能是默认的或受保护的吗?

在上面的实例中,这两个被称为“随机”的方法实际上可能需要放置在它们自己的类中,进行测试,然后注入到上面的类中。

请从行为的角度来考虑这个问题,而不是从存在哪些方法的角度来考虑。如果 b为 true,则称为 method的方法具有特定的行为。如果 b是假的,它有不同的行为。这意味着您应该为 method编写两个不同的测试; 每种情况一个。因此,没有三个面向方法的测试(一个用于 method,一个用于 method1,一个用于 method2) ,而是有两个面向行为的测试。

与此相关(我最近在另一个 SO 线程中提出了这个建议,结果得到了一个四个字母的单词,所以你可以对此保持怀疑) ; 我发现选择反映我正在测试的行为的测试名称比选择方法的名称更有帮助。所以不要调用你的测试 testMethod()testMethod1()testMethod2()等等。我喜欢像 calculatedPriceIsBasePricePlusTax()taxIsExcludedWhenExcludeIsTrue()这样的名字,它们指示我正在测试的行为; 然后在每个测试方法中,只测试指示的行为。大多数此类行为只涉及对公共方法的一个调用,但可能涉及对私有方法的多个调用。

希望这个能帮上忙。

我不明白你为什么要测试私人方法。根本问题是您的公共方法具有作为返回类型的 void,因此您无法测试您的公共方法。因此,您必须测试您的私有方法。我的猜测正确吗?

一些可能的解决方案(AFAIK) :

  1. 嘲笑您的私有方法,但仍然不会“实际”测试您的方法。

  2. 验证方法中使用的对象的状态。大多数方法要么对输入值进行一些处理并返回输出,要么更改对象的状态。还可以使用测试对象的期望状态。

    public class A{
    
    
    SomeClass classObj = null;
    
    
    public void publicMethod(){
    privateMethod();
    }
    
    
    private void privateMethod(){
    classObj = new SomeClass();
    }
    
    
    }
    

    [在这里,您可以通过检查 classObj 从 null 到 not null 的状态更改来测试 private 方法。]

  3. 稍微重构一下代码(希望这不是遗留代码)。我写方法的基本原理是,应该总是返回一些东西(int/a boolean)。返回的值可能不会被实现使用,但是它肯定会被测试使用

    密码。

    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() {}
    
    
    }
    

这里有一个如何使用 Powermock的小例子

public class Hello {
private Hello obj;
private Integer method1(Long id) {
return id + 10;
}
}

要测试 方法1使用代码:

Hello testObj = new Hello();
Integer result = Whitebox.invokeMethod(testObj, "method1", new Long(10L));

要设置私有对象 对不起,请使用:

Hello testObj = new Hello();
Hello newObject = new Hello();
Whitebox.setInternalState(testObj, "obj", newObject);

我能够测试一个私人的方法内使用仿真使用反射。 下面是这个例子,试图给它命名,使它有意义

//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));

虽然 Mockito 没有提供这个功能,但是您可以使用 Mockito + JUnit RefectionUtils 类或 Spring RefectionTestUtils 反射测试工具类实现相同的结果。请参阅下面从 给你摘录的示例,解释如何调用私有方法:

ReflectionTestUtils.invokeMethod(student, "saveOrUpdate", "From Unit test");

完整的反射测试工具和 Mockito 的例子可以在书中找到。

正式文件

实际上有一种方法可以用 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();会用一些你可以监视的东西来存根私人会员。

  1. 通过使用反射,可以从测试类调用私有方法。 在这种情况下,

    //测试方法将像这样..。

    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);
    
    
    }
    }
    
  2. If the private method calls any other private method, then we need to spy the object and stub the another method.The test class will be like ...

    //test method will be like this ...

    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);
    
    
    }
    }
    

**The approach is to combine reflection and spying the object. **method1 and **method2 are private methods and method1 calls method2.

在私有方法不为空并且返回值被用作外部依赖项方法的参数的情况下,您可以模拟依赖项并使用 ArgumentCaptor来捕获返回值。 例如:

ArgumentCaptor<ByteArrayOutputStream> csvOutputCaptor = ArgumentCaptor.forClass(ByteArrayOutputStream.class);
//Do your thing..
verify(this.awsService).uploadFile(csvOutputCaptor.capture());
....
assertEquals(csvOutputCaptor.getValue().toString(), "blabla");

基于@aravind-yarram 的回答: 不可能通过模仿。来自他们的 维基百科

那么 OO 测试私有方法的方法是什么呢?具有复杂逻辑的私有方法可能是一个信号,表明您的类违反了单一责任的原则,并且某些逻辑应该移动到一个新的类中。

事实上,通过将这些私有方法提取到更细粒度的类的公共方法中,您可以对它们进行单元测试,而不必破坏原始类的封装。