3

I have the following Singleton that returns an instance of a different class. I want to mock the method on that returned instance object. I've been reading on PowerMock mocking features for final classes and singletons but I don't know if my case falls under those or not. I appreciate some suggestion.

public final class SomeWrapper {
    private MyActualObject MyActualObject;
    private static final SomeWrapper instance = new SomeWrapper();

    private SomeWrapper() {
        // singleton
    }

    public static SomeWrapper getInstance() {
        return instance;
    }

    public void setMyActualObject(MyActualObject MyActualObject) {
        if(this.MyActualObject == null) {
            this.MyActualObject = MyActualObject;
        } else {
            throw new UnsupportedOperationException("MyActualObject is already set, cannot reset.");
        }
    }

    public MyActualObject getMyActualObject() {
        return MyActualObject;
    }
}

So now in my unit test, I wanna mock the following line:

when(SomeWrapper.getInstance().getMyActualObject().isActive()).thenReturn(false);

Should I mock the SomeWrapper and MyActualObject? Any code sample as guidance?

AlexCon
  • 1,127
  • 1
  • 13
  • 31
  • 2
    You should not use the Java singelton pattern in the first place. (this does not mean that your application should not have singelton objects...) Make the sigelton class a normal class with a public constructor. – Timothy Truckle Nov 15 '16 at 21:41

2 Answers2

4

Using these Mockito & PowerMock versions:

testImplementation 'org.mockito:mockito-core:2.23.4'
testImplementation 'org.powermock:powermock-module-junit4:2.0.0-beta.5'
testImplementation 'org.powermock:powermock-api-mockito2:2.0.0-beta.5'

You are able to mock an android MainApplication singleton instance (for example) using PowerMock.mockStatic() and the when() mechanism, like this:

private MainApplication mApplication;

@NonNull
protected MainApplication getApplication() {
    if (mApplication == null) {
        mApplication = PowerMockito.mock(MainApplication.class);

        PowerMockito.mockStatic(MainApplication.class);
        PowerMockito.when(MainApplication.getInstance()).thenReturn(mApplication);
        PowerMockito.when(mApplication.getBaseContext()).thenReturn(mApplication);
        PowerMockito.when(mApplication.getApplicationContext()).thenReturn(mApplication);
    }
    return mApplication;
}

Mocking the private constructor could be tricky - you might try the approach suggested in this post, by using a substitute class in the thenReturn() call: SO - Mocking Private Constructor

HelloImKevo
  • 547
  • 6
  • 11
0

The point here is: you wrote hard-to-test code. And yes, PowerMock can help "fixing" that, but you might prefer to simply rework your production code to make it easier to test.

For example, by adding a package protected constructor like:

SomeWrapper(MyActualObject myActualObject) {
  this.MyActualObject = myActualObject;
}

This would allow you to create an instance of SomeWrapper class that receives a completely valid MyActualObject instance.

In that sense: you might want to learn how to create production code that is actually testable.

GhostCat
  • 137,827
  • 25
  • 176
  • 248