How to mock MIDP RecordStore
Challenge
PowerMock is a mocking framework that claims to have almost supernatural powers. According to its documentation it is able to mock both static and private methods, final classes, and other nasty things that would be insurmountable obstacles for other mock frameworks. As a result, it has been stated that it should be able to mock the MIDP RecordStore, but so far I have not seen a working example. I accepted the challenge.
Getting Started
I decided that writing a unit test was the best way to get started. After all, mocking is usually used together with unit testing. In order to use RecordStore you must first open it. According to the javadoc of RecordStore, the method to be used is: public static RecordStore openRecordStore(String recordStoreName, boolean createIfNecessary)
. A static method that returns an instance of the RecordStore object. Following the “Mocking static methods” guidelines on the PowerMock web site, it seemed to be a pretty straight forward task:
- Use the
@RunWith
annotation at the class-level of the test case. - Use the
@PrepareForTest
annotation at the class-level of the test case. - Use
PowerMock.mockStatic()
to mock all methods of this class. - Use
PowerMock.replay()
to change the class to replay mode. - Use
PowerMock.verify()
to change the class to verify mode.
Ok, a little more complicated than my ordinary EasyMock setup, but nothing to really worry about. In my case, I also needed to create a mock object of the RecordStore class itself because of the return value of the openRecordStore()
method.
The Setback
To my disappointment, my test failed with an ExceptionInInitializerError
. I studied the code thoroughly to make sure that I had followed the instructions, but I concluded that error resided elsewhere. A closer look at the failure trace revealed:
[...]
Caused by: java.lang.UnsupportedOperationException: getSlowingFactor is native
at javax.microedition.rms.RecordStore.getSlowingFactor(RecordStore.java)
at javax.microedition.rms.RecordStore.(RecordStore.java:2414)
[...]
Hmm, that is strange… According to the API, there should be no getSlowingFactor()
in RecordStore? And it seems like it is called by the class initializer? When searching for an answer it seemed like this problem occurs if you have not configured the preverifier correctly. It sort of makes sense, I am not using a preverifier at all in my project and I did not like the idea of introducing one.
Solution
Returning to the PowerMock documentation, I discovered instructions how to “Suppressing Unwanted Behavior”, maybe this was the way forward? Soon, I found the annotation @SuppressStaticInitializationFor
, I added it to my test case and voilà , I had successfully mocked the RecordStore!
Reference Code
You can find the code needed to mock the RecordStore below. If you find it interesting, you can download a more complex example where a class that uses RecordStore for persistent storage is unit tested with aid of PowerMock.
import static org.easymock.EasyMock.expect;
import static org.easymock.classextension.EasyMock.createMock;
import static org.junit.Assert.assertEquals;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)
@PrepareForTest(RecordStore.class)
@SuppressStaticInitializationFor("javax.microedition.rms.RecordStore")
public class RecordStoreMockTest {
@Test
public void testCreateRecordStoreMock() throws RecordStoreException {
String recordStoreName = "record_store_name";
// Create mocks
PowerMock.mockStatic(RecordStore.class);
RecordStore recordStoreMock = createMock(RecordStore.class);
// Record behavior
expect(RecordStore.openRecordStore(recordStoreName, true)).andReturn(
recordStoreMock);
// Replay behavior
PowerMock.replay(RecordStore.class);
// Execute test and verify result
assertEquals(recordStoreMock, RecordStore.openRecordStore(recordStoreName, true));
PowerMock.verify(RecordStore.class);
}
}