关于java:使用mockito测试私有方法

Testing Private method using mockito

1
2
3
4
5
6
7
8
9
10
11
12
public class A {

    public void method(boolean b){
          if (b == true)
               method1();
          else
               method2();
    }

    private void method1() {}
    private void method2() {}
}
1
2
3
4
5
6
7
8
9
public class TestA {

    @Test
    public void testMethod() {
      A a = mock(A.class);
      a.method(true);
      //how to test like    verify(a).method1();
    }
}

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


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

Why Mockito doesn't mock private methods?

Firstly, we are not dogmatic about mocking private methods. We just
don't care about private methods because from the standpoint of
testing private methods don't exist. Here are a couple of reasons
Mockito doesn't mock private methods:

It requires hacking of classloaders that is never bullet proof and it
changes the api (you must use custom test runner, annotate the class,
etc.).

It is very easy to work around - just change the visibility of method
from private to package-protected (or protected).

It requires me to spend time implementing & maintaining it. And it
does not make sense given point #2 and a fact that it is already
implemented in different tool (powermock).

Finally... Mocking private methods is a hint that there is something
wrong with OO understanding. In OO you want objects (or roles) to
collaborate, not methods. Forget about pascal & procedural code. Think
in objects.


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


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

1
2
3
4
5
6
public class Hello {
    private Hello obj;
    private Integer method1(Long id) {
        return id + 10;
    }
}

要测试method1,请使用代码:

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

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

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


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

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

希望这可以帮助。


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

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

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


我能够使用反射在mockito中测试一个私有方法。
这是一个例子,试图将它命名为有意义

1
2
3
4
5
6
7
8
9
10
11
12
//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 ReflectionUtils类或Spring ReflectionTestUtils类获得相同的结果。请参阅下面的示例,其中解释了如何调用私有方法:

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

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


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

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

  • 嘲笑你的私人方法,但你仍然不会"实际"测试你的方法。

  • 验证方法中使用的对象的状态。 MOSTLY方法要么对输入值进行一些处理并返回输出,要么改变对象的状态。也可以使用测试对象的所需状态。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class A{

    SomeClass classObj = null;

    public void publicMethod(){
       privateMethod();
    }

    private void privateMethod(){
         classObj = new SomeClass();
    }

    }

    [Here you can test for the private method, by checking the state change of the classObj from null to not null.]

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

    码。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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() {}

    }

  • 将您的测试放在同一个包中,但是使用不同的源文件夹(src / main / java与src / test / java)并将这些方法设置为package-private。 Imo可测试性比隐私更重要。


    实际上有一种方法可以通过Mockito测试私人会员的方法。 假设你有一个这样的类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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调用一个方法,你可以写下面的内容。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @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();会将私人成员与您可以监视的内容联系起来。