Search code examples
spring-bootjunitmockitopowermockpowermockito

How to mock private method in public method in Spring Boot with JUnit


I'd like you ask a few questions and ask you for advice:

I want to test my public method (I use Spring Boot, Mockito, JUnit):

@Service
public class MyClass{

public Long getClientId(List<String> nameSurname) throws AuthorizationException {
        Long operatorId;
        if(...){
        (... something not interesting ...)
            User user = getUserByLogin("AnthonyGates2");
            operatorId = nonNull(user) ? user.getOperatorId() : null;
        } else {
            List<User> users = getUserListByLogin("AnthinyGates");
            operatorId = isNotEmpty(users) ? return operatorId;
         return operatorId;
    }

How to test the method getClientId?

Methods getUserByLogin and getUserListByLogin are private in this class (MyClass) but I have to mock the results of these private methods because these methods retrieve data from an external service. These private methods looks like:

User user = DelegateImpl.getDelegate().getUserByLogin(nameAndSurname);

DelegateImpl.getDelegate().getUserByLogin get data from database and that data have to be mocked like:

when(DelegateImpl.getDelegate().getUserByLogin(any())).thenReturn(user);

How can I test my public class? Should I use PowerMock/PowerMockito? Making these methods public is in my opinion ugly because these methods are called only in MyClass. I can't find a good tutorial in Internet for my case (Spring Boot, Mockito, JUnit).

Thank you very much for all your tips!

Best regards Matthew


Solution

  • If you are not able to unit test a method/class then it most probably means that it just does too much. Try extracting your private methods to a separate class. It does not need to be public - you can e.g. have it package-local in the same package.

    Later, in the test, you would have to inject a mock of this class and simulate its behaviour.

    The setup of MyClass in its unit test could look similar to this:

    AnotherClass anotherClassMock = Mockito.mock(AnotherClass.class);
    MyClass myClass = new MyClass(anotherClassMock);
    

    Where AnotherClass would have methods getUserListByLogin and getUserByLogin.

    EDIT: It seems that the logic within in your private methods already call an external class. The problem is that you obtain an instance of an object via a call to a static getDelegate() method in another class.

    Here's what you can do:

    • Create a new field in MyClass which would have the same type as the one returned by getDelegate() method (I don't know what that is, I'll call it Delegate)
    • Have 2 constructors: a default one which would assign the result of getDelegate method to your new field and another one which would take an instance of Delegate as a parameter and assign it to your field
    • In tests use the second constructor to create an instance of MyClass and pass a mock of Delegate class

    It would look more ore less like this:

    class MyClass() {
        private Delegate delegate;
    
        MyClass() {
            this.delegate = DelegateImpl.getDelegate();
        }
    
        MyClass(Delegate delegate) {
            this.delegate = delegate;
        }
        // ... the rest
    }