Unit Test – Using PowerMock with FileNet

To write true unit test, and make sure your FileNet developments do not rely on FileNet for unit test, one solution is using PowerMock, because it allows to mock the static methods from FileNet, that we are using a lot since there are quite usual in the FileNet Java API, for example all the fetInstance and getInstance of the Factory objects, when creating Batches and so on.

How to set up PowerMock for your project

If you are not using any project management software to manage your dependencies like Maven, you’ll have to download the PowerMockito dependencies on the PowerMock website.

PowerMockito can work with both Mockito and EasyMock, you can use which one you are familiar with. If you are not familiar with any of them, I would recommend Mockito.  I prefer the Mockito syntax, I think it makes test easier to understand and maintain. You only have to take the zip for what you want to do, in our case the  powermock-mockito-junit zip.

Then add all zip to your classpath and you are good to go.

How works PowerMock

PowerMock changes byte code at runtime to modify access to the method, fields and allow us to mock static class and method and call private method. Therefore it needs to know that it has to prepare these classes. To tell him that, there is an annotation, which is @PrepareForTest. You have to prepare the following class: static classes, final classes, classes with final method, classes you want to intercept new calls, and pretty much every class that Mockito can’t mock. I’ll let you read the PowerMock documentation to see what it can and can’t do, but it can do a lot, that’s quite amazing!

To prepare one class, it looks like that

@PrepareForTest(UpdatingBatch.class)

If you have several class (most of the time you’ll have several), do this

@PrepareForTest({Factory.Document.class, Factory.Folder.class, UpdatingBatch.class, P8PropertyHelper.class})

Then you have to run your test with the PowerMock runner so it can understand annotation and compute classes.

@RunWith(PowerMockRunner.class)

And that’s it, you are ready to mock anything. I won’t explain how to use PowerMock since it would be pasting the PowerMock documentation, so read it on their website. Instead I would give you practical examples for FileNet, with some comment so you can write your own tests.

FileNet examples

Delete empty folder method

This is really simple method but it is nice to begin with, so you can get use to the Mockito/PowerMockito syntax. The method is:

/**
 * Delete a folder if it exists, if it doesn't that's fine too, nothing thrown
 * @param os the object store to use to fetche the folder
 * @param path the folder's path
 */
public void deleteEmptyFolder(ObjectStore os, String path) {
    try{
        Folder f = Factory.Folder.fetchInstance(os, path, null);
        f.delete();
        f.save(RefreshMode.NO_REFRESH);
    } catch (EngineRuntimeException e) {}
}

We want to test this without calling FileNet. We need to check:

  • When the folder exist, if the delete method then the save method are called
  • When the folder doesn’t exist, if the method does what it says, meaning not throwing an exception

Well let’s stop talking and here is the commented code for this test:

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.when;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import com.filenet.api.constants.RefreshMode;
import com.filenet.api.core.Factory;
import com.filenet.api.core.Folder;
import com.filenet.api.core.ObjectStore;
import com.filenet.api.property.PropertyFilter;

@RunWith(PowerMockRunner.class)
@PrepareForTest(Factory.Folder.class)
public class Tests {

    @Mock private ObjectStore os;
    @Mock private Folder folder;

    private UnderTest underTest = new UnderTest();

    @Before
    public void setUp() {
        // We tell PowerMockito that we want to mock all static method of this class
        // It means that every call to static method will now we redirected to stub
        // returning null, 0, false
        // Note that we Prepare the class for test in the class level annotation
        PowerMockito.mockStatic(Factory.Folder.class);
    }

    @Test
    public void testDeleteEmptyFolder() {
        // Now we tell our mocked FileNet to return a mocked folder when the method will ask for it
        when(Factory.Folder.fetchInstance(eq(os), eq("/My/Folder/Path"), any(PropertyFilter.class))).thenReturn(folder);

        // Call the method under test
        underTest.deleteEmptyFolder(os, "/My/Folder/Path");

        // And finally make our verification
        // check delete have been called in this order
        InOrder inOrder = inOrder(folder);
        inOrder.verify(folder).delete();
        inOrder.verify(folder).save(any(RefreshMode.class));

        // Now if the folder does not exist
        when(Factory.Folder.fetchInstance(eq(os), eq("/My/Folder/Path"), any(PropertyFilter.class))).thenThrow(new EngineRuntimeException());
        underTest.deleteEmptyFolder(os, "/My/Folder/Path");
        // Nothing to check if we're not dead we're good
    }
}

I let the import for the first example so you can see what we are using. Also notice the @Mock annotation, really convenient. It creates new mock, naming them with the name of the attribute, which makes the understanding of test logs when they fail a lot easier. The important steps here are the line 36 and 42. At the line 36, me mock the static class, which means all calls made on static methods will now be made to a mock object. At he line 42, we tell PowerMock that when the static method,Factory.Folder.fetchInstance with the matching parameters, we want to return our mocked object. We do the same thing on line 54, but this time we tell PowerMock to throw an exception to simulate that the folder does not exist on the repository.

How to test when Updating batch are used

Now we will see how to intercept creation of UpdatingBatch and inject mock so we can verify interaction (especially adding objects and saving changed). We will use the same method than the previous example, but this time we will use an UpdatingBatch to delete the folder. The method is:

public void deleteEmptyFolderWithUB(ObjectStore os, String path) {
    UpdatingBatch ub = UpdatingBatch.createUpdatingBatchInstance(os.get_Domain(), RefreshMode.NO_REFRESH);
    Folder f;
    try {
        f = Factory.Folder.fetchInstance(os, path, null);
    } catch (EngineRuntimeException e) {
        return;
    }
    f.delete();
    ub.add(f, null);
    ub.updateBatch();
}

Now we will test this almost like we did before, but this in this case we need to mock the createUpdatingBatch method to return our mock. Also the verification is a little bit different since we want to make sure the object has been deleted, then added to the batch, then the batch has been saved.

@RunWith(PowerMockRunner.class)
@PrepareForTest({Factory.Folder.class, UpdatingBatch.class})
public class Tests {

    @Mock private ObjectStore os;
    @Mock private Folder folder;
    @Mock private UpdatingBatch ub;
    @Mock private Domain domain;
    @Rule public ExpectedException thrown = ExpectedException.none();

    private UnderTest underTest = new UnderTest();

    @Before
    public void setUp() {
        // We tell PowerMockito that we want to mock all static method of this class
        // It means that every call to static method will now we redirected to stub
        // returning null, 0, false
        // Note that we Prepare the class for test in the class level annotation
        PowerMockito.mockStatic(Factory.Folder.class);
        PowerMockito.mockStatic(UpdatingBatch.class);
    }

    @Test
    public void testDeleteEmptyFolderWithUB() {
        // Now we tell our mocked FileNet to return a mocked folder when the method will ask for it
        when(Factory.Folder.fetchInstance(eq(os), eq("/My/Folder/Path"), any(PropertyFilter.class))).thenReturn(folder);

        // Inject our mocked UB when it is created in the method
        when(UpdatingBatch.createUpdatingBatchInstance(eq(domain), any(RefreshMode.class))).thenReturn(ub);
        // Also the domain comes from the Object Store, so we need to return it
        when(os.get_Domain()).thenReturn(domain);

        // Call the method under test
        underTest.deleteEmptyFolderWithUB(os, "/My/Folder/Path");

        // And finally make our verification
        // check delete has been called, then the object has been added to the batch, then the batch has been saved
        InOrder inOrder = inOrder(folder, ub);
        inOrder.verify(folder).delete();
        inOrder.verify(ub).add(eq(folder), any(PropertyFilter.class));
        inOrder.verify(ub).updateBatch();

        // Now if the folder does not exist
        when(Factory.Folder.fetchInstance(eq(os), eq("/My/Folder/Path"), any(PropertyFilter.class))).thenThrow(new EngineRuntimeException());
        underTest.deleteEmptyFolderWithUB(os, "/My/Folder/Path");
        // Nothing to check if we're not dead we're good

        // Now if there is something wrong with the batch
        // We can even explicitly tell Mockito we want an exception to be thrown if the batch fails
        when(Factory.Folder.fetchInstance(eq(os), eq("/My/Folder/Path"), any(PropertyFilter.class))).thenReturn(folder);
        doThrow(new EngineRuntimeException()).when(ub).updateBatch();
        thrown.expect(EngineRuntimeException.class);
        underTest.deleteEmptyFolderWithUB(os, "/My/Folder/Path");
    }
}

As you can see on line 29, we can intercept the call of the static method creating the updating Batch to inject our mock, and be able to test any interactions with the batch.

 

Leave a Reply