ChatGPT解决这个技术问题 Extra ChatGPT

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?


K
KeatsPeeks

Not possible through mockito. From their wiki

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.


There is a fatal assumption made by that statement: >Mocking private methods is a hint that there is something wrong with OO understanding. If I'm testing a public method, and it calls private methods, I would want to mock private method returns. Going by the above assumption obviates the need to even implement private methods. How is that a poor understanding of OO?
@eggmatters According to Baeldung "Mocking techniques should be applied to the external dependencies of the class and not to the class itself. If mocking of private methods is essential for testing our classes, it usually indicates a bad design." Here is a cool thread about it softwareengineering.stackexchange.com/questions/100959/…
if you see object as the thing to test it might be right. but if you want to test functionality then the functions are the thing you want to go for. functions are for modularisation and having a module would mean to verify it first to a sane degree before verifying anything that uses it - this eases and improves overall testing results quality.
The OO 'trick' to test private method logic is to actually to create new classes having those private methods as public methods. This way you can unit test your new more granular classes, testing the previously private logic. Complex private method logic can indeed be a sign that your class has more than one responsibility and might be better broken down in more granular classes.
D
David Newcomb

You can't do that with Mockito but you can use Powermock to extend Mockito and mock private methods. Powermock supports Mockito. Here's an example.


I am confused with this answer. This is mocking, But the title is testing the private methods
I have used Powermock to mock the private method, but how can I test the private method with Powermock. Where, I can pass some input and expect some output from the method and then verify the output?
You cant.You mock input output, you cannot test the real functionality.
@diyoda_ The author wants to verify the method is called, that that has to be done by mocking. You can verify mocks only unfortunately.
M
Mindaugas Jaraminas

Here is a small example how to do it with powermock

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

To test method1 use code:

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

To set private object obj use this:

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

Your link is only pointing to the power mock repo @Mindaugas
@Xavier true. You can use it if you like in your project.
Wonderful !!! so well explained with these simple example almost everything :) Because the purpose is just to test the code and not what all the framework provides :)
Please update this. Whitebox is no longer part of the public API.
R
Rajat

While Mockito doesn't provide that capability, you can achieve the same result using Mockito + the JUnit ReflectionUtils class or the Spring ReflectionTestUtils class. Please see an example below taken from here explaining how to invoke a private method:

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

Complete examples with ReflectionTestUtils and Mockito can be found in the book Mockito for Spring.

Official documentation Spring Testing


ReflectionTestUtils.invokeMethod(student, "saveOrUpdate", "argument1", "argument2", "argument3" ); The last argument of invokeMethod, uses Vargs which can take multiple arguments that needs to passed to the private method. it works.
This answer should have more upvotes, by far the easiest way to test private methods.
S
Singh Arun

By using reflection, private methods can be called from test classes. In this case, //test method will be like this ... 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); } } 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.


The above mentioned work perfectly. Thank you
D
Dawood ibn Kareem

Think about this in terms of behaviour, not in terms of what methods there are. The method called method has a particular behaviour if b is true. It has different behaviour if b is false. This means you should write two different tests for method; one for each case. So instead of having three method-oriented tests (one for method, one for method1, one for method2, you have two behaviour-oriented tests.

Related to this (I suggested this in another SO thread recently, and got called a four-letter word as a result, so feel free to take this with a grain of salt); I find it helpful to choose test names that reflect the behaviour that I'm testing, rather than the name of the method. So don't call your tests testMethod(), testMethod1(), testMethod2() and so forth. I like names like calculatedPriceIsBasePricePlusTax() or taxIsExcludedWhenExcludeIsTrue() that indicate what behaviour I'm testing; then within each test method, test only the indicated behaviour. Most such behaviours will involve just one call to a public method, but may involve many calls to private methods.

Hope this helps.


J
Jaco Van Niekerk

You're not suppose to test private methods. Only non-private methods needs to be tested as these should call the private methods anyway. If you "want" to test private methods, it may indicate that you need to rethink your design:

Am I using proper dependency injection? Do I possibly needs to move the private methods into a separate class and rather test that? Must these methods be private? ...can't they be default or protected rather?

In the above instance, the two methods that are called "randomly" may actually need to be placed in a class of their own, tested and then injected into the class above.


A valid point. However, isn't it the reason for using a private modifier for methods is because you simply want to chop down codes that are too lengthy and/or repetitive? Separating it as another class is like you're promoting those lines of codes to be first class citizens which won't get reused anywhere else because it was meant specifically to partition lengthy codes and to prevent repeating lines of codes. If you're going to separate it to another class it just doesn't feel right; you would easily get class explosion.
Noted supertonsky, I was referring to the general case. I agree that in the above case it should not be in a separate class. (+1 on your comment though - it is a very valid point you're making on promoting private members)
@supertonsky, I have been unable to find any satisfactory response to this problem. There are several reasons why I might use private members and very often they do not indicate a code smell and I would benefit greatly from testing them. People seem to brush this off by saying "just don't do it".
Sorry, I opted to downvote based on "If you 'want' to test private methods, it may indicate you need to downvote your design". OK, fair enough, but one of the reasons to be testing at all is because, under a deadline when you don't have time to be rethinking the design, you're trying to safely implement a change that needs to be made to a private method. In an ideal world would that private method not need to be changed because the design would be perfect? Sure, but in a perfect world, but it's moot because in a perfect world who needs tests, it all just works. :)
@John. Point taken, your downvote warranted (+1). Thanks for the comment as well - I agree with you on the point you make. In such cases I can see one of two options: Either the method is made package-private or protected and unit tests written as usual; or (and this is tongue-in-cheek and bad practice) a main method is quickly written to make sure it still works. However, my response was based on a scenario of NEW code being written and not refactoring when you may not be able to tamper with the original design.
A
Abdullah Choudhury

I was able to test a private method inside using mockito using reflection. Here is the example, tried to name it such that it makes sense

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

R
Reji

I don't really understand your need to test the private method. The root problem is that your public method has void as return type, and hence you are not able to test your public method. Hence you are forced to test your private method. Is my guess correct??

A few possible solutions (AFAIK):

Mocking your private methods, but still you won't be "actually" testing your methods. Verify the state of object used in the method. MOSTLY methods either do some processing of the input values and return an output, or change the state of the objects. Testing the objects for the desired state can also be employed. 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.] Refactor your code a little (Hope this is not a legacy code). My funda of writing a method is that, one should always return something (a int/ a boolean). The returned value MAY or MAY NOT be used by the implementation, but it will SURELY BE used by the test code. 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() {} }


F
Fan Jin

There is actually a way to test methods from a private member with Mockito. Let's say you have a class like this:

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

If you want to test a.method will invoke a method from SomeOtherClass, you can write something like below.

@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(); will stub the private member with something you can spy on.


R
Roland Schneider

Put your test in the same package, but a different source folder (src/main/java vs. src/test/java) and make those methods package-private. Imo testability is more important than privacy.


The only legitimate reason is to test a part of a legacy system. If you begin to test private/package-private method, you expose your object internals. Doing so usually results in poor refactorable code. PRefer composition so you can achieve testability, with all the goodness of an object oriented system.
Agreed - that would be the preferred way. However, if you really want to test private methods with mockito, this is the only (typesafe) option you have. My answer was a bit hasty though, I should have pointed out the risks, like you and the others have done it.
This is my preferred way. There is nothing wrong to expose object internal in package-private level; and unit test is white-box testing, you need to know the internal for testing.
m
milensky

In cases where the private method is not void and the return value is used as a parameter to an external dependency's method, you can mock the dependency and use an ArgumentCaptor to capture the return value. For example:

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

H
Heschoon

Building on @aravind-yarram's answer: Not possible through mockito. From their wiki

So what's the OO way of testing private methods? Private methods with complex logic might be a sign that your class is violating the principle of single responsibility and that some of the logic should be moved to a new class.

Indeed, by extracting those private methods to public methods of more granular classes, you can unit test them without breaking the encapsulation of your original class.