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<TweetEntity, ContentValuesWrapper> toTweetContentValuesMapper;
private final TwoToOneMapper<SearchFilterEntity, TweetEntity, ContentValuesWrapper>
toTweetAndSearchFilterContentValuesMapper;
private final Mapper<Cursor, TweetEntity> toTweetMapper;
TweetByFilterDatabaseRepository(DatabaseFacade database,
Mapper<TweetEntity, ContentValuesWrapper> toTweetContentValuesMapper,
TwoToOneMapper<SearchFilterEntity, TweetEntity, ContentValuesWrapper>
toTweetAndSearchFilterContentValuesMapper,
Mapper<Cursor, TweetEntity> toTweetMapper) {
this.database = database;
this.toTweetContentValuesMapper = toTweetContentValuesMapper;
this.toTweetAndSearchFilterContentValuesMapper = toTweetAndSearchFilterContentValuesMapper;
this.toTweetMapper = toTweetMapper;
}
public void addAll(SearchFilterEntity filter, List<TweetEntity> 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,
TweetEntity tweet) throws MappingException {
final ContentValuesWrapper tweetAndSearchFilterContentValues
= toTweetAndSearchFilterContentValuesMapper.mapOrThrow(filter, tweet);
database.insertOrIgnore(TweetSearchFilterSchema.TABLE_NAME, tweetAndSearchFilterContentValues);
}
}
I want to test the two functionalities, that each Tweet is inserted to the database and that each pair is inserted to the database. I am testing the first functionality like so:
@RunWith(MockitoJUnitRunner.class)
public class TweetByFilterDatabaseRepositoryTest {
@Mock private DatabaseFacade database;
@Mock private Mapper<TweetEntity, ContentValuesWrapper> toTweetContentValuesMapper;
@Mock private TwoToOneMapper<SearchFilterEntity, TweetEntity, ContentValuesWrapper>
toTweetAndSearchFilterContentValuesMapper;
@Mock private Mapper<Cursor, TweetEntity> toTweetMapper;
private TweetByFilterDatabaseRepository tweetByFilterDatabaseRepository;
@Before
public void beforeEach() {
tweetByFilterDatabaseRepository = new TweetByFilterDatabaseRepository(database, toTweetContentValuesMapper, toTweetAndSearchFilterContentValuesMapper, toTweetMapper);
}
@Test
public void addingAllItems_withoutErrors_addsTweetsToDatabase() throws MappingException {
SearchFilterEntity filter = mock(SearchFilterEntity.class);
List<TweetEntity> tweets = mockedListOf(TweetEntity.class);
List<ContentValuesWrapper> contentValues = mockedListOf(ContentValuesWrapper.class);
for (int i = 0; i < tweets.size(); i++) {
TweetEntity tweet = tweets.get(i);
when(toTweetContentValuesMapper.mapOrThrow(tweet)).thenReturn(contentValues.get(i));
}
tweetByFilterDatabaseRepository.addAll(filter, tweets);
for (int i = 0; i < tweets.size(); i++) {
TweetEntity tweet = tweets.get(i);
ContentValuesWrapper contentValuesWrapper = contentValues.get(i);
verify(toTweetContentValuesMapper).mapOrThrow(tweet);
verify(database).insertOrUpdate(TweetSchema.TABLE_NAME, contentValuesWrapper, TweetSchema.COLUMN_ID);
}
}
}
Is there a better way to test this without the for loops or is maybe my design flawed? I am primarily interested in removing the loops from the unit test but I am also interested in improving the design of the main class if it is needed.
mockedListOf
? This isn't a Mockito method. \$\endgroup\$@InjectMocks
instead of the manual constructor call. \$\endgroup\$