Use Case
I have a service that does processing of records from multiple third-party feeds. The steps are generally identical for each feed, but each feed is populated in a different location on an aggregate feed. I decided to write a generic test that would allow testing of all this processing to keep things DRY.
Test
The test basically tests whether the processor
method in our service properly adapts and adds the incoming record to the appropriate aggregate list.
@Mock AggregateFeed dataFeed; // contains lists of POJOs
@Mock Exchange exchange; // camel mock.. not really important
@Mock SportsAdapter sportsAdapter; // used to convert string to POJO
@Mock BusinessAdapter businessAdapter; // used to convert string to POJO
FeedService fixture = new FeedService();
...
private <T1, T2> void shouldProcessRecord(BiConsumer<T1, Exchange> processor,
T1 incomingRecord, T2 fakeAdaptee, Function<T1, T2> mockAdapter,
Function<AggregateFeed, List<T2>> list) {
List<T2> fakeList = Lists.newArrayList();
when(mockAdapter.apply(incomingRecord)).thenReturn(fakeAdaptee);
when(list.apply(dataFeed)).thenReturn(fakeList);
processor.accept(incomingRecord, exchange);
assertEquals(1, fakeList.size());
assertSame(fakeAdaptee, fakeList.get(0));
}
@Test
public void shouldProcessSports() {
String testRecordFeed = constructSportsRecordFeed();
SportsRecord fakeSportsRecord = new SportsRecord();
shouldProcessRecord(fixture::addSportsRecord, testRecordFeed,
fakeSportsRecord, sportsAdapter::fromFeed, AggregateFeed::getSports);
}
@Test
public void shouldProcessBusiness() {
String testRecordFeed = constructBusinessRecordFeed();
BusinessRecord fakeBusinessRecord = new BusinessRecord();
shouldProcessRecord(fixture::addBusinessRecord, testRecordFeed,
fakeBusinessRecord, businessAdapter::fromFeed, AggregateFeed::getBusiness);
}
// builds up a csv delimited string that represents a sports article
private String constructSportsRecordFeed() {
return new StringBuilder().append( ... ).toString();
}
// builds up a csv delimited string that represents a business article
private String constructBusinessRecordFeed() {
return new StringBuilder().append( ... ).toString();
}
Concerns
The main problem I have doing things this way is that the signature just doesn't look natural. If another developer comes to create a test, they'll have to dig through the code to figure out how to use this test. On the other hand, it's much easier to do a copy and paste of the test and change around the variables, but that doesn't feel right either because it's not DRY.
Questions
- How can this generic test be improved to be easier to use?
- Since this is a test, should a copy-and-paste methodology be better since it's more direct on what the test does?
- Any other advice/comments would be greatly appreciated.
testRecordFeed
the same astestSportsRecord
, i.e. a typo? Where/what issportsAdapter
? Maybe it'll help to also show us the implementation for at leastconstructSportsRecordFeed()
, if notshouldProcessBusiness()
too? These seems to be the critical missing pieces... \$\endgroup\$constructSportsRecordFeed
because basically it's a very large csv string. Didn't want to clutter the code. \$\endgroup\$