Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
OperatorExtensions tests with MockK
import io.mockk.clearAllMocks
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runBlockingTest
import kotlinx.coroutines.test.setMain
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.*
import org.junit.jupiter.api.extension.ExtendWith
@ExtendWith(InstantExecutorExtension::class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class OperatorExtensionsTest {
//Documentation is your friend: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/
@ExperimentalCoroutinesApi
private val testDispatcher = TestCoroutineDispatcher()
@BeforeAll
@ExperimentalCoroutinesApi
fun setupDispatchers() {
Dispatchers.setMain(testDispatcher)
}
@AfterAll
@ExperimentalCoroutinesApi
fun clearDispatchers() {
Dispatchers.resetMain()
testDispatcher.cleanupTestCoroutines()
}
@AfterEach
fun clear() {
clearAllMocks()
}
@Test
@ExperimentalCoroutinesApi
fun `When throttleFirst is called, Then it will emmit the first item and skip all the following emissions until a certain time interval is respected`() {
var timesReturned = 0
lateinit var throttleFirstFx: (Unit) -> Unit
testDispatcher.runBlockingTest {
throttleFirstFx = throttleFirst(1_000, this, action = { timesReturned++ })
repeat(3) { throttleFirstFx(Unit) } //only one emission will hapen
assertThat(timesReturned).isEqualTo(1)
repeat(3) {
delay(1_000)
throttleFirstFx(Unit) //3 emissions
}
}
assertThat(timesReturned).isEqualTo(4) //(1+3)
}
@Test
@ExperimentalCoroutinesApi
fun `When throttleLatest is called, Then it returns the latest data after a time interval without emissions`() {
var valueReturned = 0
lateinit var throttleLatestFx: (Int) -> Unit
testDispatcher.runBlockingTest {
throttleLatestFx = throttleLatest(500, this, action = { value -> valueReturned = value })
throttleLatestFx(1)
throttleLatestFx(2)
throttleLatestFx(3)
assertThat(valueReturned).isEqualTo(0) //nothing changed so far, we didn't respect the 500ms interval of emission, yet...
delay(500) //Note that without runBlockingTest 520ms is the minimum precision
assertThat(valueReturned).isEqualTo(3) //now that we have and the last emitted was throttleLatestFx(3), valueReturned will be 3
throttleLatestFx(4)
}
assertThat(valueReturned).isEqualTo(4) //the runBlocking will wait until completion so valueReturned will be 4.
}
@Test
@ExperimentalCoroutinesApi
fun `When debounce is called it, Then returns only if there's no new data emission for a desired time interval`() {
var timesReturned = 0
val action: (Unit) -> Unit = { timesReturned++ }
lateinit var debounceFx: (Unit) -> Unit
testDispatcher.runBlockingTest {
debounceFx = debounce(500, this, action = action)
repeat(2) {
debounceFx(Unit)
}
delay(600) //now that we've waited at least >500ms, let's emit the latest call, thus timesReturned = 1
assertThat(timesReturned).isEqualTo(1)
repeat(2) {
debounceFx(Unit)
delay(600) //each cycle we wait 600ms, all emissions will be fired, thus timesReturned = (1+2)
}
}
assertThat(timesReturned).isEqualTo(3)
}
@Test
@ExperimentalCoroutinesApi
fun `When throttleClick is used, Then all supersonic clicks will be avoided, useful for UI`() {
var timesReturned = 0
testDispatcher.runBlockingTest {
val clickFx = throttleClick( this, clickAction = { timesReturned++ })
repeat(100) { clickFx(Unit) }
}
assertThat(timesReturned).isEqualTo(1)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment