Skip to content

Commit

Permalink
Refactor testing API to improve readability
Browse files Browse the repository at this point in the history
  • Loading branch information
yigitozgumus committed Jul 3, 2024
1 parent ae7c4ca commit 476f842
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 129 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package com.trendyol.transmission.features.colorpicker
import androidx.compose.ui.graphics.Color
import com.trendyol.transmission.transformer.util.TestCoroutineRule
import com.trendyol.transmission.ui.ColorPickerUiState
import com.yigitozgumus.transmissiontesting.testWithEffect
import com.yigitozgumus.transmissiontesting.testWithSignal
import com.yigitozgumus.transmissiontesting.attachToRouter
import com.yigitozgumus.transmissiontesting.test
import org.junit.Before
import org.junit.Rule
import org.junit.Test
Expand All @@ -25,45 +25,49 @@ class ColorPickerTransformerTest {

@Test
fun `GIVEN transformer, WHEN BackgroundColorUpdate effect is received, THEN color should be changed 1`() {
sut.testWithEffect(effect = ColorPickerEffect.BackgroundColorUpdate(Color.Gray)) {
assertEquals(
ColorPickerUiState(backgroundColor = Color.Gray),
dataStream.last()
)
}
sut.attachToRouter()
.test(effect = ColorPickerEffect.BackgroundColorUpdate(Color.Gray)) {
assertEquals(
ColorPickerUiState(backgroundColor = Color.Gray),
dataStream.last()
)
}
}

@Test
fun `GIVEN transformer, WHEN BackgroundColorUpdate effect is received, THEN color should be changed`() =
sut.testWithEffect(effect = ColorPickerEffect.BackgroundColorUpdate(Color.Gray)) {
assertEquals(
ColorPickerUiState(backgroundColor = Color.Gray),
dataStream.last()
)
}
sut.attachToRouter()
.test(effect = ColorPickerEffect.BackgroundColorUpdate(Color.Gray)) {
assertEquals(
ColorPickerUiState(backgroundColor = Color.Gray),
dataStream.last()
)
}

@Test
fun `GIVEN inputTransformer, WHEN SelectColor signal is sent, THEN selectedColorIndex should be updated`() =
sut.testWithSignal(signal = ColorPickerSignal.SelectColor(3, Color.Blue)) {
assertEquals(
3,
(dataStream.last() as ColorPickerUiState).selectedColorIndex
)
}
sut.attachToRouter()
.test(ColorPickerSignal.SelectColor(3, Color.Blue)) {
assertEquals(
3,
(dataStream.last() as ColorPickerUiState).selectedColorIndex
)
}

@Test
fun `GIVEN inputTransformer, WHEN SelectColor signal is sent, THEN BackgroundColorUpdate effect should be published`() {
sut.testWithSignal(signal = ColorPickerSignal.SelectColor(3, Color.Blue)) {
assertEquals(
Color.Blue.copy(alpha = 0.1f),
(effectStream.first().effect as ColorPickerEffect.BackgroundColorUpdate).color
)
}
sut.attachToRouter()
.test(signal = ColorPickerSignal.SelectColor(3, Color.Blue)) {
assertEquals(
Color.Blue.copy(alpha = 0.1f),
(effectStream.first().effect as ColorPickerEffect.BackgroundColorUpdate).color
)
}
}

@Test
fun `GIVEN inputTransformer, WHEN SelectColor signal is sent, THEN SelectedColorUpdate is sent to MultiOutputTransformer`() {
sut.testWithSignal(signal = ColorPickerSignal.SelectColor(3, Color.Blue)) {
sut.attachToRouter().test(signal = ColorPickerSignal.SelectColor(3, Color.Blue)) {
assertTrue { effectStream.last().effect is ColorPickerEffect.SelectedColorUpdate }
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import androidx.compose.ui.graphics.Color
import com.trendyol.transmission.features.colorpicker.ColorPickerEffect
import com.trendyol.transmission.transformer.util.TestCoroutineRule
import com.trendyol.transmission.ui.InputUiState
import com.yigitozgumus.transmissiontesting.testWithEffect
import com.yigitozgumus.transmissiontesting.testWithSignal
import com.yigitozgumus.transmissiontesting.attachToRouter
import com.yigitozgumus.transmissiontesting.test
import org.junit.Before
import org.junit.Rule
import kotlin.test.Test
Expand All @@ -25,16 +25,18 @@ class InputTransformerTest {

@Test
fun `GIVEN inputTransformer, WHEN inputUpdate signal is sent, THEN inputUpdate effect is published`() {
sut.testWithSignal(signal = InputSignal.InputUpdate("test")) {
assertEquals(InputEffect.InputUpdate("test"), effectStream.first().effect)
assertEquals(InputUiState("test"), dataStream.last())
}
sut.attachToRouter()
.test(signal = InputSignal.InputUpdate("test")) {
assertEquals(InputEffect.InputUpdate("test"), effectStream.first().effect)
assertEquals(InputUiState("test"), dataStream.last())
}
}

@Test
fun `GIVEN inputTransformer, WHEN BackgroundColorUpdate effect is received, THEN color should be changed`() {
sut.testWithEffect(effect = ColorPickerEffect.BackgroundColorUpdate(Color.Gray)) {
assertEquals(InputUiState(backgroundColor = Color.Gray), dataStream.last())
}
sut.attachToRouter()
.test(effect = ColorPickerEffect.BackgroundColorUpdate(Color.Gray)) {
assertEquals(InputUiState(backgroundColor = Color.Gray), dataStream.last())
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import com.trendyol.transmission.features.input.InputEffect
import com.trendyol.transmission.transformer.util.TestCoroutineRule
import com.trendyol.transmission.ui.ColorPickerUiState
import com.trendyol.transmission.ui.OutputUiState
import com.yigitozgumus.transmissiontesting.testWithEffect
import com.yigitozgumus.transmissiontesting.attachToRouter
import com.yigitozgumus.transmissiontesting.test
import org.junit.Before
import org.junit.Rule
import org.junit.Test
Expand All @@ -26,25 +27,29 @@ class OutputTransformerTest {

@Test
fun `GIVEN sut, WHEN inputUpdate effect comes, THEN, holder should be updated with correct value`() {
sut.testWithEffect(effect = InputEffect.InputUpdate("test")) {
assertEquals(OutputUiState(outputText = "test"), dataStream.last())
}
sut.attachToRouter()
.test(effect = InputEffect.InputUpdate("test")) {
assertEquals(OutputUiState(outputText = "test"), dataStream.last())
}
}

@Test
fun `GIVEN sut, WHEN inputUpdate effect comes and no ColorPickerUIState data is present, THEN data should not be updated further`() {
sut.testWithEffect(effect = InputEffect.InputUpdate("test")) {
assertEquals(OutputUiState(outputText = "test"), dataStream.last())
}
sut.attachToRouter()
.test(effect = InputEffect.InputUpdate("test")) {
assertEquals(OutputUiState(outputText = "test"), dataStream.last())
}
}

@Test
fun `GIVEN sut, WHEN inputUpdate effect comes and ColorPickerUIState exists, THEN RouterPayloadEffect should be published`() {
sut.testWithEffect(effect = InputEffect.InputUpdate("test"), registry = {
addQueryData(ColorPickerUiState(), key = "ColorPickerUiState")
}) {
assertEquals(OutputUiState(outputText = "test"), dataStream[1])
assertTrue(effectStream.last().effect is RouterEffect)
}
sut.attachToRouter()
.register {
addQueryData(ColorPickerUiState(), key = "ColorPickerUiState")
}
.test(effect = InputEffect.InputUpdate("test")) {
assertEquals(OutputUiState(outputText = "test"), dataStream[1])
assertTrue(effectStream.last().effect is RouterEffect)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.launch

internal class TestRouter(
private val registry: RegistryScopeImpl,
private val transformer: Transformer,
dispatcher: CoroutineDispatcher
) {
private val testScope = CoroutineScope(dispatcher)
var registry: RegistryScopeImpl = RegistryScopeImpl()

private val signalBroadcast = testScope.createBroadcast<Transmission.Signal>()
private val effectBroadcast = testScope.createBroadcast<EffectWrapper>()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.yigitozgumus.transmissiontesting

import com.trendyol.transmission.Transmission
import com.trendyol.transmission.effect.EffectWrapper
import com.trendyol.transmission.transformer.Transformer
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest

class TestSuite {
private var orderedInitialProcessing: List<Transmission> = emptyList()
private var transformer: Transformer? = null
private lateinit var router: TestRouter

@OptIn(ExperimentalCoroutinesApi::class)
fun initialize(transformer: Transformer): TestSuite {
this.transformer = transformer
router = TestRouter(transformer, UnconfinedTestDispatcher())
return this
}

fun register(registry: RegistryScope.() -> Unit = {}): TestSuite {
router.registry = RegistryScopeImpl().apply(registry)
return this
}

fun processBeforeTesting(vararg transmissions: Transmission): TestSuite {
orderedInitialProcessing = transmissions.toList()
return this
}

@OptIn(ExperimentalCoroutinesApi::class)
fun runTest(
transmission: Transmission,
scope: suspend TransformerTestScope.(scope: TestScope) -> Unit
) {

runTest {
val dataStream: MutableList<Transmission.Data> = mutableListOf()
val effectStream: MutableList<EffectWrapper> = mutableListOf()
try {
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
router.dataStream.toList(dataStream)
}
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
router.effectStream.toList(effectStream)
}
val testScope = object : TransformerTestScope {
override val dataStream: List<Transmission.Data> = dataStream
override val effectStream: List<EffectWrapper> = effectStream
}
orderedInitialProcessing.forEach {
when (it) {
is Transmission.Data -> throw IllegalArgumentException("Transmission.Data should not be sent for processing")
is Transmission.Effect -> router.sendEffect(it)
is Transmission.Signal -> router.sendSignal(it)
}
transformer?.waitProcessingToFinish()
}
if (transmission is Transmission.Signal) {
router.sendSignal(transmission)
} else if (transmission is Transmission.Effect) {
router.sendEffect(transmission)
}
transformer?.waitProcessingToFinish()
testScope.scope(this)
} finally {
advanceUntilIdle()
router.clear()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,96 +1,25 @@
package com.yigitozgumus.transmissiontesting

import com.trendyol.transmission.Transmission
import com.trendyol.transmission.effect.EffectWrapper
import com.trendyol.transmission.transformer.Transformer
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest

@OptIn(ExperimentalCoroutinesApi::class)
fun Transformer.testWithEffect(
orderedInitialProcessing: List<Transmission> = emptyList<Transmission>(),
fun Transformer.attachToRouter(): TestSuite {
return TestSuite().initialize(this)
}

fun TestSuite.test(
effect: Transmission.Effect,
registry: RegistryScope.() -> Unit = {},
scope: suspend TransformerTestScope.(scope: TestScope) -> Unit
) {
runTest {
val registryImpl = RegistryScopeImpl().apply(registry)
val testRouter = TestRouter(registryImpl, this@testWithEffect, UnconfinedTestDispatcher())
val dataStream: MutableList<Transmission.Data> = mutableListOf()
val effectStream: MutableList<EffectWrapper> = mutableListOf()
try {
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
testRouter.dataStream.toList(dataStream)
}
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
testRouter.effectStream.toList(effectStream)
}
val testScope = object : TransformerTestScope {
override val dataStream: List<Transmission.Data> = dataStream
override val effectStream: List<EffectWrapper> = effectStream
}
orderedInitialProcessing.forEach {
when (it) {
is Transmission.Data -> throw IllegalArgumentException("Transmission.Data should not be sent for processing")
is Transmission.Effect -> testRouter.sendEffect(it)
is Transmission.Signal -> testRouter.sendSignal(it)
}
this@testWithEffect.waitProcessingToFinish()
}
testRouter.sendEffect(effect)
this@testWithEffect.waitProcessingToFinish()
testScope.scope(this)
} finally {
advanceUntilIdle()
testRouter.clear()
}
}
this.runTest(effect, scope)
}

@OptIn(ExperimentalCoroutinesApi::class)
fun Transformer.testWithSignal(
orderedInitialProcessing: List<Transmission> = emptyList<Transmission>(),
fun TestSuite.test(
signal: Transmission.Signal,
registry: RegistryScope.() -> Unit = {},
scope: suspend TransformerTestScope.(scope: TestScope) -> Unit
) {
runTest {
val registryImpl = RegistryScopeImpl().apply(registry)
val testRouter = TestRouter(registryImpl, this@testWithSignal, UnconfinedTestDispatcher())
val dataStream: MutableList<Transmission.Data> = mutableListOf()
val effectStream: MutableList<EffectWrapper> = mutableListOf()
try {
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
testRouter.dataStream.toList(dataStream)
}
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
testRouter.effectStream.toList(effectStream)
}
val testScope = object : TransformerTestScope {
override val dataStream: List<Transmission.Data> = dataStream
override val effectStream: List<EffectWrapper> = effectStream
}
orderedInitialProcessing.forEach {
when (it) {
is Transmission.Data -> throw IllegalArgumentException("Transmission.Data should not be sent for processing")
is Transmission.Effect -> testRouter.sendEffect(it)
is Transmission.Signal -> testRouter.sendSignal(it)
}
this@testWithSignal.waitProcessingToFinish()
}
testRouter.sendSignal(signal)
this@testWithSignal.waitProcessingToFinish()
testScope.scope(this)
} finally {
advanceUntilIdle()
testRouter.clear()
}
}
this.runTest(signal, scope)
}

suspend fun Transformer.waitProcessingToFinish() {
Expand Down

0 comments on commit 476f842

Please sign in to comment.