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.
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.
ReplyDeleteIt 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.
ReplyDelete