Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Static factory methods and mocking

How do you reconcile using static factory methods and mocking?

Many people would just say: Don't use static factory methods, use DI instead.

Well, sometimes you cannot avoid static factory methods. Consider the following use cases, which should be familiar:

Imagine you have a class called Option, like in scala. You can't avoid using a static factory method if you want to reuse same instance for all absent values.

As soon as you go new Option(null) you create a new option object, you cannot return the same object over and over again.

Similar use case is the Integer.valueOf() which will reuse integer objects for values below 128. Impossible to do without using a static factory method.

Another advantage is that factory methods are more descriptive than new keyword.

So how do you guys deal with having to use static factory methods and at the same time wanting to use inheritance and mocks?

Thank you.

like image 412
RokL Avatar asked Dec 01 '15 16:12

RokL


3 Answers

Since it's a theorical question, I will make a theorical answer. The factory paradigm is the building point for another theory: the Injection. If your created objects are injected when needed, then you only have to inject your mocked objects to do all your tests. There alot of good books / web pages that can help you to get started on that.

like image 157
JFPicard Avatar answered Oct 23 '22 17:10

JFPicard


Mocking out static methods is possible using PowerMock. Consider the following example from their Wiki page:

@Test
public void testRegisterService() throws Exception {
    long expectedId = 42;

    // We create a new instance of test class under test as usually.
    ServiceRegistartor tested = new ServiceRegistartor();

    // This is the way to tell PowerMock to mock all static methods of a
    // given class
    mockStatic(IdGenerator.class);

    /*
     * The static method call to IdGenerator.generateNewId() expectation.
     * This is why we need PowerMock.
     */
    expect(IdGenerator.generateNewId()).andReturn(expectedId);

    // Note how we replay the class, not the instance!
    replay(IdGenerator.class);

    long actualId = tested.registerService(new Object());

    // Note how we verify the class, not the instance!
    verify(IdGenerator.class);

    // Assert that the ID is correct
    assertEquals(expectedId, actualId);
}

It's even possible to mock out only one particular method and leave the rest as is, using partial mocking.

like image 2
jabu.10245 Avatar answered Oct 23 '22 18:10

jabu.10245


My first option is to avoid the need to mock anything, so having static factory methods or not makes no difference.

That said, if I do want or need to mock them, then I just do it. For example, consider you are testing a JSF-based web application, and you want to mock the javax.faces.context.FacesContext object. I would write the following in a test, using the JMockit library (which I happen to develop):

@Test
public void exampleTest(@Mocked final FacesContext ctx) {
    // Call the code under test, which will at some point
    // call FacesContext.getCurrentInstance(), then add an
    // error message for display in the web page.

    new Verifications() {{
        FacesMessage msg;
        ctx.addMessage(null, msg = withCapture());

        assertEquals("The expected error message.", msg.getSummary());
        assertNotNull(msg.getDetail());
    }};
}

In this example, Faces.getCurrentInstance() is the static factory method, which will automatically return a mock FacesContext instance once the class is mocked.

like image 1
Rogério Avatar answered Oct 23 '22 18:10

Rogério