1
\$\begingroup\$

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:

  1. getByFilter receives a SearchFilterEntity and a Page
  2. These are mapped to a NetworkFilter with the use of filterAndPageToNetworkFilterMapper#mapOrThrow
  3. Then the twitterApi gets the NetworkFilter and uses it to return some Tweets
  4. Finally, these Tweets are mapped to TweetEntity 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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.