Matelutex
Matelutex

Reputation: 2210

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

Upvotes: 2

Views: 9008

Answers (2)

Poger
Poger

Reputation: 1937

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
}

Upvotes: 0

Jasper Huzen
Jasper Huzen

Reputation: 1573

Test the unit only by calling the public methods. I think that your example is a class in the service layer (contains business logic) and the two getUser... methods should be in a different class (I think in the data layer) where they can be public. Inject that class via the constructor as a dependency (in the service object) so you can mock it when testing the service class. The data layer class (with the getUser... methods) can also be tested by it's own unit tests.

Upvotes: 1

Related Questions