Testing a subclass-as-wrapper

The problem

I’m working with an oldie-but-goodie framework. This framework has some interesting naming conventions. Say for instance, they call subclasses wrappers. We can use subclasses and wrappers for the same thing. However, one of them is more testable than the other.

Let’s see the similarities first. Suppose you have a MessageProcessor class and that has a transformMessage method:

public class MessageProcessor {
    public Action transformMessage(IMessage message) {
        //....
    }
}

You can enhance this behavior with a subclass:

public class ExtraMessageProcessor extends MessageProcessor {
    public Action transformMessage(IMessage message) {
        // before
        String originalFrom = message.getFrom();
        message.setImportantFlag(true);

        // then
        Action result = super.transformMessage(message);
        // clean coders don't like super calls. Wonder why?

        //after
        result.getTransformedMessage().setFrom(originalFrom);

        // finally
        return result;
    }
}

The code for a wrapper is almost the same

public class ExtraMessageProcessor implements IMessageProcessor {

    private IMessageProcessor originalProcessor;

    public Action transformMessage(IMessage message) {
        // before
        String originalFrom = message.getFrom();
        message.setImportantFlag(true);

        // then
        Action result = originalProcessor.transformMessage(message);

        //after
        result.getTransformedMessage().setFrom(originalFrom);

        // finally
        return result;
    }
}

See, there are only two differences:

  • The subclass has a super call while the wrapper doesn’t.
  • The wrapper needs an extracted interface or some other abstraction

Testability

Okay, we have some code. Let’s do some development-driven testing:


@Test
public void testSubclass() {
    // GIVEN
    ExtraMessageProcessor proc = new ExtraMessageProcessor();
    IMessage msg = createExampleMessage();

    // WHEN
    Action action = proc.transformMessage(msg);

    // THEN - doing some assertions
}

What do you think about it? is it good? Is it bad?

I really don’t like that this test depends on what the superclass does. There’s no way I could mock out or spy the super method without mocking out the current method I’m testing. So this test is complex.

Also, when we update the framework, the vendor of the framework can change the superclass. Hence this test is brittle too.

Unorthodox wrapper

See, the superclass comes from the vendor. Thus we cannot extract any abstraction from it. Let’s simulate a wrapper in a bit complicated way:

public class WrapperMessageProcessor extends MessageProcessor {
    private final MessageProcessor wrapped;

    public WrapperMessageProcessor(MessageProcessor wrapped) {
        this.wrapped = wrapped;
    }

    public Action transformMessage(IMessage message) {
        // before ...
        // then
        Action result = wrapped.transformMessage(message);
        //after ...
        // finally
        return result;
    }
}

So we can write tests for this:


@Test
public void testSubclassAsWrapper() {
    // GIVEN
    MessageProcessor parentProc = Mokcito.spy(new MessageProcessor());
    configureParentMethodForSpy(parentProc);
    WrapperMessageProcessor sut = new WrapperMessageProcessor(parentProc);
    IMessage msg = createExampleMessage();

    // WHEN
    Action action = proc.transformMessage(msg);

    // THEN - doing some assertions
}

This looks quite good, doesn’t it? Okay, using spy is usually a code smell, but this comes from the framework. The code is ugly too. Anyway, we can test our code in isolation, and that’s cool.

There are some real drawbacks though. First of all, we have to teach the framework to use the WrapperMessageProcessor wherever it would use the MessageProcessor. This isn’t obvious, but we might solve it in a couple of hours.

As it turns out, this framework uses lots of static initialization blocks with this MessageProcessor. Those initialization blocks read stuff from property files and from databases. I tried to use Powermocks whenNew as poor mans Dependency Injection framework. After some hours of hacking, it was obviously a dead end: the code was ugly, and the tests too. Moreover, the tests would break at any slight change on the framework side.

An framework-independent class

The third attempt was to factor out the logic to a new class:

public class EnhancedMessageProcessor extends MessageProcessor {
    private WrapperLogic logic = new WrapperLogic(); // remember, no DI

    public Action transformMessage(IMessage message) {
        return logic.transformMessage(this, message);
    }
}

public class WrapperLogic {
    public Action transformMessage(EnhancedMessageProcessor proc, IMessage message) {

        // before ...
        // then
        Action result = proc.super.transformMessage(message); // oops
        //after ...
        // finally
        return result;
    }
}

All looks fine. Except, that we cannot call proc.super.transformMessage(); Calling proc.transformMessage() gives a StackOverflowError, so, that’s a no-go.

Some lexical closure

What now? Closures to the rescue! In java we have anonymous inner classes. In java 8 we have lamdas too, as syntactic sugar. I’m showing the classes in different order now:

public class WrapperLogic {
    public Action transformMessage(IMessage message, Function<IMessage, Action> superCall) {

        // before ...
        // then
        Action result = superCall.apply(message); // cool now
        //after ...
        // finally
        return result;
    }
}

public class EnhancedMessageProcessor extends MessageProcessor {
    private WrapperLogic logic = new WrapperLogic(); // remember, no DI

    public Action transformMessage(IMessage message) {
        return logic.transformMessage(message, (IMessage msg) -> {
            return super.transformMessage(msg);
        });
    }
}

So, we can write independent tests for the WrapperLogic:


@Test
public void testSubclassAsWrapper() {
    // GIVEN
    Function<IMessage, Action> superCall = new Function<IMessage, Action>() {
        // ...
    };
    MessageProcessor parentProc = Mokcito.spy(new MessageProcessor());
    WrapperLogic sut = new WrapperLogic();
    IMessage msg = createExampleMessage();

    // WHEN
    Action action = sut.transformMessage(msg);

    // THEN - doing some assertions
}

This is really cool. Finally we do have Dependency Injection – on the method level. We can test the wrapper logic in full isolation.

The only thing we cannot test is the subclass that calls this wrapper logic. We cannot change that. All we can do is to mitigate that risk:

  • we keep that class very thin
  • we add end-to-end smoke tests to make sure the right wrapper methods are actually invoked

Lessons learned:

  • Frameworks with Dependency Injection are more flexible than those without it.
  • You can still add Dependency Injection to your code.
  • You have to do some trade-off. At testing too.

How do you attack this sort of problems? Is there anything else in your toolbox that I haven’t tried?

 

Advertisements

About tamasrev

A software developer, specialized in Java, addressing himself as generalist. A proud daddy.
This entry was posted in java, programming and tagged , , , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s