I have this class with one method, which uses three different objects to perform a task.
class TweetOfficialNetworkRepository implements TweetNetworkRepository {
private final TwitterApi twitterApi;
private final Mapper<Tweet, TweetEntity> tweetToTweetEntityMapper;
private final TwoToOneMapper<SearchFilterEntity, Page, NetworkFilter> filterAndPageToNetworkFilterMapper;
TweetOfficialNetworkRepository(TwitterApi twitterApi,
Mapper<Tweet, TweetEntity> tweetToTweetEntityMapper,
TwoToOneMapper<SearchFilterEntity, Page, NetworkFilter> filterAndPageToNetworkFilterMapper) {
this.twitterApi = twitterApi;
this.tweetToTweetEntityMapper = tweetToTweetEntityMapper;
this.filterAndPageToNetworkFilterMapper = filterAndPageToNetworkFilterMapper;
}
@Override
public Single<List<TweetEntity>> getByFilter(final SearchFilterEntity filter, final Page page) {
return Single.defer(new Callable<Single<NetworkFilter>>() {
@Override
public Single<NetworkFilter> call() {
return Single.just(filterAndPageToNetworkFilterMapper.mapOrThrow(filter, page));
}
}).flatMap(new Func1<NetworkFilter, Single<List<Tweet>>>() {
@Override
public Single<List<Tweet>> call(NetworkFilter networkFilter) {
return twitterApi.search(networkFilter);
}
}).map(new Func1<List<Tweet>, List<TweetEntity>>() {
@Override
public List<TweetEntity> call(List<Tweet> tweets) {
return tweetToTweetEntityMapper.mapAllAndSkipOnFailedMapping(tweets);
}
});
}
}
What happens is:
getByFilter
receives aSearchFilterEntity
and aPage
- These are mapped to a
NetworkFilter
with the use offilterAndPageToNetworkFilterMapper#mapOrThrow
- Then the
twitterApi
gets theNetworkFilter
and uses it to return someTweet
s - Finally, these
Tweet
s are mapped toTweetEntity
and returned to the caller.
Right now I am testing this functionality one-by-one.
@RunWith(MockitoJUnitRunner.class)
public class TweetOfficialNetworkRepositoryTest {
@Mock private TwitterApi twitterApi;
@Mock private Mapper<Tweet, TweetEntity> tweetToTweetEntityMapper;
@Mock private TwoToOneMapper<SearchFilterEntity, Page, NetworkFilter> filterAndPageToNetworkFilterMapper;
private TweetNetworkRepository repository;
private SearchFilterEntity filter;
private Page page;
@Before
public void beforeEach() {
repository = new TweetOfficialNetworkRepository(twitterApi, tweetToTweetEntityMapper,
filterAndPageToNetworkFilterMapper);
filter = defaultSearchFilter();
page = defaultPage();
}
@Test
public void gettingItems_mapsFilterAndPageToNetworkFilter() throws MappingException {
repository.getByFilter(filter, page).subscribe(DummySubscriber.newInstance());
verify(filterAndPageToNetworkFilterMapper, only()).mapOrThrow(filter, page);
}
@Test
public void gettingItems_usesTwitterApiForSearching() throws MappingException {
NetworkFilter networkFilter = defaultNetworkFilter();
haveNetworkFilterMapperReturnFilter(networkFilter);
repository.getByFilter(filter, page).subscribe(DummySubscriber.newInstance());
verify(twitterApi, only()).search(networkFilter);
}
@Test
public void gettingItems_mapsTweetsToTweetEntities() throws MappingException {
NetworkFilter networkFilter = defaultNetworkFilter();
List<Tweet> tweets = mockedListOf(Tweet.class);
haveNetworkFilterMapperReturnFilter(networkFilter);
haveTwitterApiReturnTweets(networkFilter, tweets);
repository.getByFilter(filter, page).subscribe(DummySubscriber.newInstance());
verify(tweetToTweetEntityMapper, only()).mapAllAndSkipOnFailedMapping(tweets);
}
@Test
public void gettingItems_ReturnsItems() throws MappingException {
NetworkFilter networkFilter = defaultNetworkFilter();
List<Tweet> tweets = mockedListOf(Tweet.class);
List<TweetEntity> tweetEntities = mockedListOf(TweetEntity.class);
haveNetworkFilterMapperReturnFilter(networkFilter);
haveTwitterApiReturnTweets(networkFilter, tweets);
when(tweetToTweetEntityMapper.mapAllAndSkipOnFailedMapping(tweets)).thenReturn(tweetEntities);
repository.getByFilter(filter, page).subscribe(new Action1<List<TweetEntity>>() {
@Override
public void call(List<TweetEntity> tweetEntities) {
assertThat(tweetEntities).isEqualTo(tweetEntities);
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
fail("Should not have thrown error: " + throwable);
}
});
}
private void haveNetworkFilterMapperReturnFilter(NetworkFilter networkFilter) throws MappingException {
when(filterAndPageToNetworkFilterMapper.mapOrThrow(filter, page)).thenReturn(networkFilter);
}
private void haveTwitterApiReturnTweets(NetworkFilter networkFilter, List<Tweet> tweets) {
when(twitterApi.search(networkFilter)).thenReturn(Single.just(tweets));
}
}
defaultSearchFilter()
, defaultPage()
and defaultNetworkFilter()
provide some premade objects for usage in tests. DummySubscriber.newInstance()
provides a Subscriber
which does nothing in onNext()
, onCompleted()
and onError()
.
My main question would be if it's best to test this functionality one-by-one or all at once. Any other suggestions to make this more readable are welcome.