HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Unit test for a method that adds tweets to a database

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
methoddatabasethatfortestaddstweetsunit

Problem

I have a class which inserts a list of tweets to a database table and also inserts the tweet's key with the associated filter's key to a many-to-many table. I am testing the sole public method of this class:

```
class TweetByFilterDatabaseRepository {

private final DatabaseFacade database;

private final Mapper toTweetContentValuesMapper;

private final TwoToOneMapper
toTweetAndSearchFilterContentValuesMapper;

private final Mapper toTweetMapper;

TweetByFilterDatabaseRepository(DatabaseFacade database,
Mapper toTweetContentValuesMapper,
TwoToOneMapper
toTweetAndSearchFilterContentValuesMapper,
Mapper toTweetMapper) {
this.database = database;
this.toTweetContentValuesMapper = toTweetContentValuesMapper;
this.toTweetAndSearchFilterContentValuesMapper = toTweetAndSearchFilterContentValuesMapper;
this.toTweetMapper = toTweetMapper;
}

public void addAll(SearchFilterEntity filter, List tweets) {
database.beginTransaction();

try {
for (TweetEntity tweet : tweets) {
try {
saveTweet(tweet);
saveTweetSearchFilterForeignKeys(filter, tweet);
} catch (MappingException e) {
//no-op
}
}
database.setTransactionSuccessful();
} finally {
database.endTransaction();
}
}

private void saveTweet(TweetEntity tweet) throws MappingException {
final ContentValuesWrapper tweetContentValues = toTweetContentValuesMapper.mapOrThrow(tweet);
database.insertOrUpdate(TweetSchema.TABLE_NAME, tweetContentValues, TweetSchema.COLUMN_ID);
}

private void saveTweetSearchFilterForeignKeys(SearchFilterEntity filter,

Solution

Disclaimer: I'm not a huge fan of mocks.

Some issues I see with the approach:

  • The fixture setup is quite complicated, involving a lot of mocks. It's a lot to read.



  • Using loops in test methods is a test smell.



  • Verifying that .mapOrThrow gets called on each tweet, and verifying that insertOrUpdate gets called with specific parameters on database call for concern. The test knows too much about intimate details of the implementation. As such, the tests are too tightly coupled to the implementation, and very likely fragile tests (another test smell). The slightest change in implementation details will likely break tests, and become a maintenance nightmare.



I suggest to take a different approach: use a fake database.
With an in-memory implementation of DatabaseFacade,
the test class could become a lot simpler, easier to read,
and eliminate the current test smells.

//@RunWith(MockitoJUnitRunner.class) -> not needed anymore -> to delete
public class TweetByFilterDatabaseRepositoryTest {

    @Test
    public void addingAllItems_withoutErrors_addsTweetsToDatabase() {
        DatabaseFacade database = newFakeDatabase();
        SearchFilterEntity filter = mock(SearchFilterEntity.class);
        List tweets = mockedListOf(TweetEntity.class);

        tweetByFilterDatabaseRepository.addAll(filter, tweets);

        assertEquals(tweets, database.getTweets());
    }
}


I mentioned at the top that I don't like mocks.
But notice that I didn't eliminate all the mocks.
I only eliminated the ones that didn't help reducing complexity.
I kept the ones that are useful, and I have no problems with those.

It should be straightforward to add further tests for the cases of some tweets with mapping errors, all tweets with mapping errors, filtering logic, and so on.

Code Snippets

//@RunWith(MockitoJUnitRunner.class) -> not needed anymore -> to delete
public class TweetByFilterDatabaseRepositoryTest {

    @Test
    public void addingAllItems_withoutErrors_addsTweetsToDatabase() {
        DatabaseFacade database = newFakeDatabase();
        SearchFilterEntity filter = mock(SearchFilterEntity.class);
        List<TweetEntity> tweets = mockedListOf(TweetEntity.class);

        tweetByFilterDatabaseRepository.addAll(filter, tweets);

        assertEquals(tweets, database.getTweets());
    }
}

Context

StackExchange Code Review Q#128287, answer score: 2

Revisions (0)

No revisions yet.