I've just started looking at Room, Coroutines, and Flow, and have come across something odd: what I'm expecting to be an empty flow actually has one null item in it.
My setup is as follows, with generic T
for my actual entities.
interface TDao {
@Query("SELECT * FROM Table WHERE date=:date")
fun getT(date: String): Flow<T>
}
@Singleton
class TRepository @Inject constructor(
private val apiService: TApiService,
private val Tdao: TDao
) {
suspend fun getTFor(date: String): Flow<T> =
Tdao
.getT(date)
.map {
if (it == null) {
returnTFromDatabase()
} else {
it
}
}
Now, when the database doesn't have any T
in it for date
, I'm expecting it to return an empty flow, with no items in it. Instead, it has one null
element, which should never happen, because T
isn't nullable.
I wrote this test for it:
@RunWith(AndroidJUnit4::class)
class TDatabaseTest {
private lateinit var db: TDatabase
private lateinit var underTest: TDao
@Before
fun setUp() {
val context = InstrumentationRegistry.getInstrumentation().context
db = Room.inMemoryDatabaseBuilder(context, TDatabase::class.java).build()
underTest = db.TDao()
}
@After
fun tearDown() {
db.close()
}
@Test
fun givenEmptyDatabase_thenHasNoItems() {
runBlocking {
val list = underTest.getT("1999").take(1).toList()
assertEquals(1, list.size)
}
}
}
...and it passes, cause, again, there's one null
item returned.
What am I missing? What's wrong here, because I can't quite figure it out. Why am I getting a single null element in a flow with non nullable elements?
Any?
(link) – DurenT
may be non-nullable, therefore it is never ok to pass anull
as aT < Any?
. You just have to remember thatT < T?
. – Knacker