Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing Room and LiveData

I'm currently developing an app using the newly Android Architecture Components. Specifically, I'm implementing a Room Database that returns a LiveData object on one of its queries. Insertion and querying work as expected, however I have an issue testing the query method using a unit test.

Here is the DAO I'm trying to test:

NotificationDao.kt

@Dao interface NotificationDao {      @Insert     fun insertNotifications(vararg notifications: Notification): List<Long>      @Query("SELECT * FROM notifications")     fun getNotifications(): LiveData<List<Notification>> } 

As you can tell, the query function returns a LiveData object, if I change this to be just a List, Cursor, or basically whatever then I get the expected result, which is the data inserted in the Database.

The issue is that the following test will always fail because the value of the LiveData object is always null:

NotificationDaoTest.kt

lateinit var db: SosafeDatabase lateinit var notificationDao: NotificationDao  @Before fun setUp() {     val context = InstrumentationRegistry.getTargetContext()     db = Room.inMemoryDatabaseBuilder(context, SosafeDatabase::class.java).build()     notificationDao = db.notificationDao() }  @After @Throws(IOException::class) fun tearDown() {     db.close() }  @Test fun getNotifications_IfNotificationsInserted_ReturnsAListOfNotifications() {     val NUMBER_OF_NOTIFICATIONS = 5     val notifications = Array(NUMBER_OF_NOTIFICATIONS, { i -> createTestNotification(i) })     notificationDao.insertNotifications(*notifications)      val liveData = notificationDao.getNotifications()     val queriedNotifications = liveData.value     if (queriedNotifications != null) {         assertEquals(queriedNotifications.size, NUMBER_OF_NOTIFICATIONS)     } else {         fail()     } }  private fun createTestNotification(id: Int): Notification {     //method omitted for brevity  } 

So the question is: Does anyone knows of a better way to perform unit tests that involve LiveData objects?

like image 389
Nicolás Carrasco-Stevenson Avatar asked May 30 '17 19:05

Nicolás Carrasco-Stevenson


People also ask

How do you observe LiveData in unit testing?

In order to test LiveData, we need to take care of two things. First, the LiveData should be observed in order to work properly. You can find a utility class at the end of the section to observe your LiveData from test class. Secondly we should add InstantExecutorRule test rule.

What is the use of InstantTaskExecutorRule?

InstantTaskExecutorRule instead. A JUnit Test Rule that swaps the background executor used by the Architecture Components with a different one which executes each task synchronously. You can use this rule for your host side tests that use Architecture Components.


1 Answers

Room calculates the LiveData's value lazily when there is an observer.

You can check the sample app.

It uses a getValue utility method which adds an observer to get the value:

public static <T> T getOrAwaitValue(final LiveData<T> liveData) throws InterruptedException {     final Object[] data = new Object[1];     final CountDownLatch latch = new CountDownLatch(1);     Observer<T> observer = new Observer<T>() {         @Override         public void onChanged(@Nullable T o) {             data[0] = o;             latch.countDown();             liveData.removeObserver(this);         }     };     liveData.observeForever(observer);     latch.await(2, TimeUnit.SECONDS);     //noinspection unchecked     return (T) data[0]; } 

Better w/ kotlin, you can make it an extensions function :).

like image 159
yigit Avatar answered Sep 21 '22 08:09

yigit