使用mockito测试私有方法

Nag*_*ran 90 java junit 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();
    }
}

如何测试私有方法是否被调用,以及如何使用mockito测试私有方法???

Ara*_*ram 125

不可能通过mockito.来自他们的维基

为什么Mockito不会模仿私有方法?

首先,我们对于嘲弄私人方法不是教条主义.我们只是不关心私有方法,因为从测试私有方法的角度来看不存在.以下是Mockito不会模仿私有方法的几个原因:

它需要对类加载器进行黑客攻击,这种类加载器绝不是防弹的,它会改变api(你必须使用自定义测试运行器,注释类等).

这很容易解决 - 只需将方法的可见性从私有更改为受包保护(或受保护).

它需要我花时间实施和维护它.鉴于第2点和它已经在不同的工具(powermock)中实现的事实,它没有意义.

最后......嘲弄私有方法暗示OO理解存在问题.在OO中,您希望对象(或角色)进行协作,而不是方法.忘记pascal和程序代码.在对象中思考.

  • 该语句做出了一个致命的假设:>模拟私有方法暗示着 OO 理解存在问题。如果我正在测试公共方法,并且它调用私有方法,我想模​​拟私有方法的返回。按照上述假设,甚至不需要_实现_私有方法。对 OO 的理解很差怎么办? (7认同)
  • 测试私有方法逻辑的面向对象“技巧”实际上是创建将这些私有方法作为公共方法的新类。通过这种方式,您可以对新的更细粒度的类进行单元测试,测试以前的私有逻辑。复杂的私有方法逻辑确实可以表明您的类具有多个职责,并且可以更好地分解为更细粒度的类。 (4认同)
  • @eggmatters 根据 Baeldung 的说法,“模拟技术应该应用于类的外部依赖项,而不是类本身。如果私有方法的模拟对于测试我们的类至关重要,那么通常表明设计很糟糕。” 这是一个关于它的很酷的线程https://softwareengineering.stackexchange.com/questions/100959/how-do-you-unit-test-private-methods (2认同)

shi*_*t66 74

你不能用Mockito做到这一点,但你可以使用Powermock来扩展Mockito和模拟私有方法.Powermock支持Mockito.是一个例子.

  • 我对这个答案很困惑.这是嘲弄,但标题是测试私人方法 (14认同)

Min*_*nas 31

这是一个如何用powermock做的小例子

public class Hello {
    private Hello obj;
    private Integer method1(Long id) {
        return id + 10;
    }
} 
Run Code Online (Sandbox Code Playgroud)

要测试method1,请使用代码:

Hello testObj = new Hello();
Integer result = Whitebox.invokeMethod(testObj, "method1", new Long(10L));
Run Code Online (Sandbox Code Playgroud)

要设置私有对象obj,请使用以下命令:

Hello testObj = new Hello();
Hello newObject = new Hello();
Whitebox.setInternalState(testObj, "obj", newObject);
Run Code Online (Sandbox Code Playgroud)


Daw*_*ica 16

从行为的角度考虑这一点,而不是根据哪些方法.method如果b为true,则调用的方法具有特定行为.如果b是假的,它有不同的行为.这意味着你应该写两个不同的测试method; 每个案例一个.因此,不是有三个面向方法的测试(一个用于method,一个用于method1,一个用于method2,你有两个面向行为的测试.

与此相关(我最近在另一个SO线程中提出了这个问题,因此得到了一个四个字母的单词,所以请随意拿出一些盐); 我发现选择反映我正在测试的行为的测试名称是有帮助的,而不是方法的名称.所以,不要打电话给你测试testMethod(),testMethod1(),testMethod2()等等.我喜欢这样的名字,calculatedPriceIsBasePricePlusTax()或者taxIsExcludedWhenExcludeIsTrue()表示我正在测试的行为; 然后在每个测试方法中,仅测试指示的行为.大多数此类行为仅涉及对公共方法的一次调用,但可能涉及对私有方法的多次调用.

希望这可以帮助.


Sin*_*run 13

  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);
    
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 如果私有方法调用任何其他私有方法,那么我们需要监视对象并存根另一个方法。测试类将类似于......

    //测试方法会是这样的...

    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);
    
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

**方法是结合反射和窥探对象。**method1 和 **method2 是私有方法,method1 调用 method2。


Jac*_*erk 6

你不应该测试私有方法.只需要测试非私有方法,因为这些方法应该调用私有方法.如果您"想"测试私有方法,则可能表明您需要重新考虑您的设计:

我使用适当的依赖注入吗?我是否可能需要将私有方法移动到单独的类中而是测试它?这些方法必须是私有的吗?......他们不能默认或保护吗?

在上面的例子中,两个被称为"随机"的方法实际上可能需要放在他们自己的类中,测试然后注入上面的类.

  • 一个有效的观点.但是,对于方法使用私有修饰符的原因是不是因为您只想删除冗长和/或重复的代码?将它分离为另一个类就像是你将这些代码行推广为一等公民,不会在其他任何地方重复使用,因为它专门用于分割冗长的代码并防止重复代码行.如果你要将它分成另一个类,它感觉不对; 你很容易上课爆炸. (23认同)
  • @supertonsky,我一直无法找到任何令人满意的回答这个问题.有几个原因可以解释为什么我可能会使用私人成员,而且他们通常不会指出代码味道,我会从测试中获益很多.人们似乎只是说"只是不要这样做". (4认同)
  • 对不起,我选择了基于"如果你想'测试私人方法的downvote,它可能表明你需要对你的设计进行投票".好吧,公平,但完全测试的原因之一是因为,在没有时间重新考虑设计的截止日期之前,您正试图安全地实施需要对私人方法.在一个理想的世界中,私人方法是否需要改变,因为设计是完美的?当然,但是在一个完美的世界里,但它没有实际意义,因为在一个需要测试的完美世界中,它只是起作用.:) (3认同)
  • 注意到supertonsky,我指的是一般情况.我同意在上述情况下,它不应该在一个单独的类中.(虽然您的评论为+1 - 这是您在宣传私人会员方面的一个非常有效的观点) (2认同)
  • @约翰.点了,你的downvote保证(+1).感谢您的评论 - 我同意您的观点.在这种情况下,我可以看到两个选项之一:方法是包私有或受保护,单元测试像往常一样; 或者(这是诙谐和不好的做法)快速编写主要方法以确保它仍然有效.但是,我的回答是基于新代码编写的情况,而不是在您可能无法篡改原始设计时进行重构. (2认同)
  • 仅仅为了在测试套件中启用功能而将方法移动到它们自己的类中,或者将它们公开,这绝对是不好的做法。并且“你不应该测试私有方法。” 我强烈不同意。私有方法有其自身的复杂性,因此测试是完全合理的。 (2认同)

AR1*_*AR1 6

虽然Mockito不提供该功能,但是您可以使用Mockito + JUnit ReflectionUtils类或Spring ReflectionTestUtils类来获得相同的结果。请参阅下面取自例如这里解释如何调用私有方法:

ReflectionTestUtils.invokeMethod(student, "saveOrUpdate", "From Unit test");
Run Code Online (Sandbox Code Playgroud)

可以在《Mockito for Spring》一书中找到ReflectionTestUtils和Mockito的完整示例。

  • 这个答案应该有更多的赞成票,这是迄今为止测试私有方法的最简单方法。 (3认同)

Rej*_*eji 5

我真的不明白您需要测试私有方法。根本问题是您的公共方法的返回类型为 void,因此您无法测试您的公共方法。因此,您被迫测试您的私有方法。我的猜测正确吗??

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

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

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

    public class A{
    
    SomeClass classObj = null;
    
    public void publicMethod(){
       privateMethod();
    }
    
    private void privateMethod(){
         classObj = new SomeClass();
    }
    
    }
    
    Run Code Online (Sandbox Code Playgroud)

    [在这里你可以测试私有方法,通过检查 classObj 从空到非空的状态变化。]

  3. 稍微重构您的代码(希望这不是遗留代码)。我编写方法的基础是,一个人应该总是返回一些东西(一个整数/一个布尔值)。返回值可能或可能不会被实现使用,但它肯定会被测试使用

    代码。

    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() {}
    
    }
    
    Run Code Online (Sandbox Code Playgroud)


Abd*_*ury 5

我能够使用反射使用嘲笑来测试内部的私有方法。这是示例,尝试命名为有意义

//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));
Run Code Online (Sandbox Code Playgroud)