Thursday, 24 October 2013

Testing different implementations of the Domain Repository interface

The post is about a simple but useful way of writing tests against different implementations of a Repository interface. Assuming you have a PlayerRepository in your Domain (this is just an interface) and two implementations in the Infrastructure layer, InMemoryPlayerRepository and MongoPlayerRepository. The latter is used in production, the former is mainly for tests.

interface PlayerRepository {
        boolean exists(PlayerId playerId); 
}
class InMemoryPlayerRepository implements PlayerRepository {
        @Override public boolean exists(PlayerId playerId) {  ...  }
}
class MongoPlayerRepository implements PlayerRepository {
        @Override public boolean exists(PlayerId playerId) {  ...  }
}
I used to maintain different test suites for them, since MongoPlayerRepository required for example a running Mongo process and a running Spring application context, while the in-memory version did not. But most tests were identical so it was simply duplicated code. The solution I'm using nowadays is extracting the tests in an abstract test template class and do implementation specific test configuration in the subclasses.

abstract class AbstractPlayerRepositoryTestTemplate {
        protected PlayerRepository testObj;
        @Test
        public void exists() { 
                // setup
                ...
                //assert
                assertTrue(testObj.exists(playerId));
        }
}
 
class InMemoryPlayerRepositoryTest extends AbstractPlayerRepositoryTestTemplate {
        @Before
        public void before() {
                testObj = new InMemoryPurchaseStore();
        }
}
 
@ContextConfiguration("classpath:applicationContext-test.xml")
@RunWith(SpringJUnit4ClassRunner.class)
class MongoPlayerRepositoryITest extends AbstractPlayerRepositoryTestTemplate {
        @Resource
        private MongoPlayerRepository mongoRepository;
        @Before
        public void before() {
                testObj = mongoRepository;
                testObj.clear();
        }
}
Of course you can use the approach for anything where there are multiple implementations of an interface, I just come across the need most frequently with Repositories.

No comments :

Post a Comment