P Kuijpers P Kuijpers - 4 months ago 13
Android Question

Field value not set when using mock and abstract class

For some unexplainable reason a field (

currentExp
) I'm assigning in the constructor isn't correctly set while using a mock for my unittest. I'm assigning the field
currentExp
by loading it via the
loadExperience
method using my
Storage
class (which uses SharedPreferences). When I'm unittesting this, I'd like to mock the
Storage
class, so
loadexperience
returns a value of 10.

Here's my concrete
Experience
class:

public class Experience extends StorageObject {

private int currentExp = 0;

public Experience() {
this(new Storage());
}

@VisibleForTesting
protected Experience(Storage storage) {
super(storage);
} // Debug point #2

@Override
protected void init(Storage storage) {
this.currentExp = storage.loadExperience();
} // Debug point #1
}


It extends
StorageObject
:

public abstract class StorageObject {
protected Storage storage;

protected StorageObject() {
this(new Storage());
}

@VisibleForTesting
protected StorageObject(Storage storage) {
this.storage = storage;
init(storage);
}

protected abstract void init(Storage storage);
}


And this is my unittest:

@Test
public void testConstructor_StorageValuePositive_IsSetAsCurrentExp() {
int expectedSavedExp = 10;
Storage storageMock = mock(Storage.class);
doReturn(expectedSavedExp).when(storageMock).loadExperience();

Experience exp = new Experience(storageMock);

assertEquals(expectedSavedExp, exp.getCurrentExp());
}


While debugging I found out the mock DOES work, and the value of 10 is assigned to
currentExp
at Debug point #1. Then shortly afterwards, at Debug point #2 the value seems to be 0 again.

Anyone got a clue what is happening here, and how to solve this problem?

Answer

The problem here is the initialization order. The super constructor happens first then the field initialization.

So your constructor sets the currentExp in it's super call to 10 then the field gets initilized with 0.

So what can you do? Some ideas: Move the currentExp to the parent class or don't give it a default value.

More reading material:

http://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.5

http://stackoverflow.com/a/14806340/5842844

Comments