-
Notifications
You must be signed in to change notification settings - Fork 57
Unit Tests improvements #137
base: main
Are you sure you want to change the base?
Changes from 4 commits
fde5b1e
f18d3f6
c60b401
0149805
9480825
74d9471
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -84,6 +84,9 @@ kotlin { | |
languageSettings.apply { | ||
optIn("kotlinx.coroutines.ExperimentalCoroutinesApi") | ||
} | ||
dependencies { | ||
implementation("com.google.truth:truth:1.0.1") | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we need another assertion library. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Well, the Truth library makes assertions more readable and easy to understand unlike the normal assertions used throughout the hole tests (from what I have seen). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know about it 😅. I just don't think we need it. Tests can be made more readable without it. Please remove it. |
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,5 +23,5 @@ import com.android254.data.db.model.Session | |
interface SessionDao : BaseDao<Session> { | ||
|
||
@Query("SELECT * FROM SESSION") | ||
fun fetchSessions(): List<Session> | ||
suspend fun fetchSessions(): List<Session> // This should be suspended or return FLow [Note: Don't suspend Flows] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove comment. We can add it to the readme instead. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @anselmoalexandre We should not suspend these queries. I think best way is to return flows instead There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jumaallan any reason why we shouldn't suspend it? 🤔 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @anselmoalexandre - maybe this explains it better - https://stackoverflow.com/a/68750634
I am just thinking we should prefer using flows instead? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yep, Flows are great. But you also need to question yourself: is it needed to use Flow? 🤔 Or I can just suspend this. 😄 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure what everyone else thinks here (I would prefer using flows 😸 ) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I support @jumaallan idea of using a flow.To better support this I can attach this link for reference https://medium.com/androiddevelopers/room-flow-273acffe5b57 |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,7 +19,7 @@ import kotlinx.coroutines.flow.Flow | |
|
||
interface TokenProvider { | ||
|
||
suspend fun fetch(): Flow<String?> | ||
suspend fun fetch(): Flow<String?> // @Allan -> Don't suspend Flows | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove comment. We can add it to the readme instead. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @anselmoalexandre Good catch Remove the suspend and return a flow 👯 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hey @chepsi There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @anselmoalexandre saw it I can raise a PR, but if you want, please feel free to refactor it here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
You can do the PR it's okay 👌🏿. Also, this was only heads up 😄 |
||
|
||
suspend fun update(accessToken: String) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,51 +15,44 @@ | |
*/ | ||
package com.android254.data.dao | ||
|
||
import android.content.Context | ||
import androidx.room.Room | ||
import androidx.test.core.app.ApplicationProvider | ||
import com.android254.data.db.Database | ||
import com.android254.data.db.model.Session | ||
import com.google.common.truth.Truth | ||
import io.mockk.* | ||
import io.mockk.impl.annotations.MockK | ||
import kotlinx.coroutines.test.runTest | ||
import kotlinx.datetime.toInstant | ||
import org.hamcrest.CoreMatchers.`is` | ||
import org.hamcrest.MatcherAssert.assertThat | ||
import org.junit.After | ||
import org.junit.Before | ||
import org.junit.Test | ||
import org.junit.runner.RunWith | ||
import org.robolectric.RobolectricTestRunner | ||
import java.io.IOException | ||
|
||
@RunWith(RobolectricTestRunner::class) | ||
class SessionDaoTest { | ||
|
||
private lateinit var session: Session | ||
@MockK | ||
private lateinit var sessionDao: SessionDao | ||
private lateinit var db: Database | ||
Comment on lines
-35
to
-39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are using Robolectric because we want to test against a real db. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @michaelbukachi With robolectric: it slows down the tests a lot, but it's sometimes helpful if we really want to test the integration with the Android system There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd suggest to use it only in a few places, not everywhere. Considering that tests performance are also very important and should be fast as possible There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
But it's okay if you guys think you want to stick with this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @anselmoalexandre We are aware of its drawbacks. We used it to ensure all our queries were running as expected. |
||
|
||
@Before | ||
fun setup() { | ||
val context = ApplicationProvider.getApplicationContext<Context>() | ||
db = Room.inMemoryDatabaseBuilder( | ||
context, | ||
Database::class.java | ||
MockKAnnotations.init(this, relaxUnitFun = true) | ||
session = Session( | ||
id = 0, | ||
name = "Welcome Keynote", | ||
publishDate = "2010-06-01T22:19:44.475Z".toInstant() | ||
) | ||
.allowMainThreadQueries() // TODO Please delete me | ||
.build() | ||
sessionDao = db.sessionDao() | ||
} | ||
|
||
@After | ||
@Throws(IOException::class) | ||
fun tearDown() { | ||
db.close() | ||
coJustRun { sessionDao.insert(session) } | ||
coEvery { sessionDao.fetchSessions() } returns listOf(session) | ||
} | ||
|
||
@Test | ||
fun `test sessionDao fetches all sessions`() = runTest { | ||
val session = Session(0, "Welcome Keynote", "2010-06-01T22:19:44.475Z".toInstant()) | ||
// Given | ||
sessionDao.insert(session) | ||
val result = sessionDao.fetchSessions().first() | ||
assertThat(session.name, `is`(result.name)) | ||
|
||
// When | ||
val result = sessionDao.fetchSessions().first().name | ||
|
||
// Then | ||
coVerify(atLeast = 1) { sessionDao.insert(session) } | ||
coVerify { sessionDao.insert(session) } | ||
Truth.assertThat(result).isEqualTo(session.name) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,12 +29,11 @@ import com.android254.data.network.models.responses.UserDetails | |
import com.android254.data.network.util.HttpClientFactory | ||
import com.android254.data.network.util.ServerError | ||
import com.android254.data.preferences.DefaultTokenProvider | ||
import com.google.common.truth.Truth | ||
import io.ktor.client.engine.mock.* | ||
import io.ktor.http.* | ||
import kotlinx.coroutines.delay | ||
import kotlinx.coroutines.runBlocking | ||
import org.hamcrest.CoreMatchers.`is` | ||
import org.hamcrest.MatcherAssert.assertThat | ||
import kotlinx.coroutines.test.runTest | ||
import org.junit.Before | ||
import org.junit.Test | ||
import org.junit.runner.RunWith | ||
|
@@ -55,19 +54,21 @@ class AuthApiTest { | |
|
||
@Test(expected = ServerError::class) | ||
fun `test ServerError is thrown when a server exception occurs`() { | ||
// Given | ||
val mockEngine = MockEngine { | ||
delay(500) | ||
respondError(HttpStatusCode.InternalServerError) | ||
} | ||
val httpClient = HttpClientFactory(DefaultTokenProvider(testDataStore)).create(mockEngine) | ||
val api = AuthApi(httpClient) | ||
runBlocking { | ||
api.logout() | ||
} | ||
|
||
// When | ||
runTest { api.logout() } | ||
} | ||
|
||
@Test | ||
fun `test successful logout`() { | ||
// Given | ||
val mockEngine = MockEngine { | ||
respond( | ||
content = """{"message": "Success"}""", | ||
|
@@ -77,14 +78,18 @@ class AuthApiTest { | |
} | ||
val httpClient = HttpClientFactory(DefaultTokenProvider(testDataStore)).create(mockEngine) | ||
val api = AuthApi(httpClient) | ||
runBlocking { | ||
val response = api.logout() | ||
assertThat(response, `is`(Status("Success"))) | ||
|
||
// Then | ||
runTest { | ||
api.logout().also { | ||
Truth.assertThat(it).isEqualTo(Status("Success")) | ||
} | ||
} | ||
} | ||
|
||
@Test | ||
fun `test successful google login`() { | ||
// Given | ||
val content = """ | ||
{ | ||
"token": "test", | ||
|
@@ -106,18 +111,23 @@ class AuthApiTest { | |
} | ||
val httpClient = HttpClientFactory(DefaultTokenProvider(testDataStore)).create(mockEngine) | ||
val api = AuthApi(httpClient) | ||
runBlocking { | ||
val accessToken = AccessToken( | ||
token = "test", | ||
user = UserDetails( | ||
name = "Magak Emmanuel", | ||
email = "[email protected]", | ||
gender = null, | ||
avatar = "http://localhost:8000/upload/avatar/img-20181016-wa0026jpg.jpg" | ||
) | ||
|
||
val accessToken = AccessToken( | ||
token = "test", | ||
user = UserDetails( | ||
name = "Magak Emmanuel", | ||
email = "[email protected]", | ||
gender = null, | ||
avatar = "http://localhost:8000/upload/avatar/img-20181016-wa0026jpg.jpg" | ||
) | ||
val response = api.googleLogin(GoogleToken("some token")) | ||
assertThat(response, `is`(accessToken)) | ||
) | ||
|
||
// Then | ||
runTest { | ||
api.googleLogin(GoogleToken("some token")).also { | ||
Truth.assertThat(it).isEqualTo(accessToken) | ||
} | ||
} | ||
|
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @anselmoalexandre For all dependencies, you need to add them to the
lib.versions file
. You can check the Readme on why we do this.Great PR!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤦🏿♂️ my bad. Let me add it to version catalog
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Added the dependency to
lib.versions
file