Tuesday, September 07, 2004

A clever production v. testing trick

Say you have a database connection provider for production, and a mock persistence layer for testing. This setup is common for iBatis, Hibernate or similar solutions. How should you design the consumers of these? I fell upon this clever pattern today while reworking an event replayer which read events from a database and injected them into a messaging system:

/**
 * <code>Foo</code> does something clever. It has two
 * modes: production and testing.  For production, use {@link
 * #Foo(ConnectionProvider)} and pass in a provider.  For testing,
 * use {@link #Foo(BarMapper)} and pass in a mock mapper.
 *
 * The code takes great care in the constructors to ensure you cannot mix uses.
 * All instance variables are marked <code>final</code>.
 *
 * If you are in production use, the constructor tests <var>provider</var> and
 * assigns it, making the mapper <code>null</code>.  Then, if you try to use
 * the mapper instance variable directly instead of via the getter for it,
 * you will throw a <code>NullPointerException</code>.
 *
 * Contrariwise, if you are in testing use, the constructor tests the
 * mapper parameter and assigns it, making the provider <code>null</code>.
 * Then, if you try to use the provider instance variable directly instead of
 * via the getter for it, you will throw a <code>NullPointerException</code>.
 *
 * Lastly, there is no {@link Connection} instance variable.  Instead {@link
 * #barNone(Id)} gets one from the provider on the fly (the getter
 * ensure this only really does something if in production mode), and closes it
 * before the method returns.  There is never a leak, the connection is never
 * held for longer than needed, the method may be run more than once
 * statelessly, and the method uses a getter for the mapper, again ensuring
 * the code <cite>does the right thing</cite>.
 */
public class Foo {
    private final ConnectionProvider provider;
    private final BarMapper mapper;

    public Foo(final ConnectionProvider provider) {
        if (null == provider)
            throw new NullPointerException();

        this.provider = provider;
        this.mapper = null;
    }

    protected Foo(final BarMapper mapper) {
        if (null == mapper)
            throw new NullPointerException();

        this.provider = null;
        this.mapper = mapper;
    }

    public void barNone(final Id barId) {
        final Connection conn = getConnection();

        try {
            doSomethingCleverHere(barId, getMapper(conn));

        } finally {
            close(conn);
        }
    }

    private Connection getConnection() {
        return null != provider
            ? provider.getConnection()
            : null;
    }

    private void close(final Connection conn) {
        if (null != provider) provider.close(conn);
    }

    private BarMapper getMapper(final Connection conn) {
        return null != mapper
            ? mapper
            : new BarMapper(conn);
    }
}

See the idea? Several things are going on at once here. Read the class javadoc comment carefully. This trick will serve you well.

A footnote: why is the one constructor public and the other protected? Simple. The constructor taking a connection provider is for production and is marked public. The constuctor taking a mapper is for testing and is marked protected. Remember to keep production and test case code in the same package but in separate source trees.

2 comments:

Anonymous said...

why not just have one constructor that takes both a provider and a mapper or even just a mapper? code would be much more readable, you can dynamically mock the provider and mapper in tests, and then you have no additional cruft just for the purpose of testing. Having a different way of constructing/using objects for tests is a big smell IMHO.

Brian Oxley said...

It turns out that the Provider is very difficult to mock. This is the reason I looked at this approach in the first place. In our production system, the provider makes a database connection, which gets you a result set, etc. The amount of mocking grows very considerably. We refactored the code to limit this so that we could either provide a provider, or provide an easy-to-mock mapper. Hence the two constructors. I should have mentioned this in the original posting.