diff --git a/binder/build.gradle b/binder/build.gradle index 11d5849d..c1dcfa63 100644 --- a/binder/build.gradle +++ b/binder/build.gradle @@ -11,8 +11,9 @@ dependencies { implementation libs.rxkotlin implementation libs.kotlin.stdlib - testImplementation libs.junit - testImplementation libs.kotlin.test.junit + testImplementation libs.junit5 + testImplementation libs.junit.params + testImplementation libs.junit.platform.launcher testImplementation libs.mockito.kotlin } @@ -24,3 +25,7 @@ compileKotlin { jvmTarget = JavaVersion.VERSION_11 } } + +test { + useJUnitPlatform() +} diff --git a/binder/src/test/java/com/badoo/binder/BinderTest.kt b/binder/src/test/java/com/badoo/binder/BinderTest.kt index d90a1d25..d6369b87 100644 --- a/binder/src/test/java/com/badoo/binder/BinderTest.kt +++ b/binder/src/test/java/com/badoo/binder/BinderTest.kt @@ -4,7 +4,7 @@ import com.badoo.binder.connector.Connector import io.reactivex.Observable import io.reactivex.ObservableSource import io.reactivex.subjects.PublishSubject -import org.junit.Test +import org.junit.jupiter.api.Test class BinderTest { diff --git a/binder/src/test/java/com/badoo/binder/LifecycleTest.kt b/binder/src/test/java/com/badoo/binder/LifecycleTest.kt index 6bcbb501..60b6c414 100644 --- a/binder/src/test/java/com/badoo/binder/LifecycleTest.kt +++ b/binder/src/test/java/com/badoo/binder/LifecycleTest.kt @@ -5,13 +5,12 @@ import com.badoo.binder.lifecycle.ManualLifecycle import io.reactivex.ObservableSource import io.reactivex.functions.Consumer import io.reactivex.subjects.PublishSubject -import org.junit.Test +import org.junit.jupiter.api.Test import org.mockito.kotlin.any import org.mockito.kotlin.mock import org.mockito.kotlin.times import org.mockito.kotlin.verify - class LifecycleTest { private val lifecycle: ManualLifecycle = Lifecycle.manual() diff --git a/binder/src/test/java/com/badoo/binder/TestConsumer.kt b/binder/src/test/java/com/badoo/binder/TestConsumer.kt index b5fad7d7..4d295e89 100644 --- a/binder/src/test/java/com/badoo/binder/TestConsumer.kt +++ b/binder/src/test/java/com/badoo/binder/TestConsumer.kt @@ -1,7 +1,7 @@ package com.badoo.binder import io.reactivex.functions.Consumer -import kotlin.test.assertEquals +import org.junit.jupiter.api.Assertions.assertEquals class TestConsumer : Consumer { val values = mutableListOf() diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3562887d..9cd96217 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -29,6 +29,8 @@ gsonVersion = "2.8.5" # Testing junitVersion = "4.12" +junit5Version = "5.9.2" +junitPlatformLauncherVersion = "1.9.2" supportTestVersion = "1.3.0" mockitoKotlinVersion = "4.0.0" @@ -81,6 +83,9 @@ gson = { module = "com.google.code.gson:gson", version.ref = "gsonVersion" } # Testing junit = { module = "junit:junit", version.ref = "junitVersion" } +junit5 = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit5Version" } +junit-params = { module = "org.junit.jupiter:junit-jupiter-params", version = "junit5Version"} +junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher", version.ref = "junitPlatformLauncherVersion" } kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlinVersion" } mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version.ref = "mockitoKotlinVersion" } androidx-test-runner = { module = "androidx.test:runner", version.ref = "supportTestVersion" } diff --git a/mvicore-android/build.gradle b/mvicore-android/build.gradle index e19a0797..31548097 100644 --- a/mvicore-android/build.gradle +++ b/mvicore-android/build.gradle @@ -34,6 +34,15 @@ android { kotlinOptions { jvmTarget = JavaVersion.VERSION_11 } + + testOptions { + unitTests { + all { + useJUnitPlatform() + } + } + } + } dependencies { @@ -45,8 +54,11 @@ dependencies { implementation libs.rxkotlin implementation libs.rxandroid - testImplementation libs.junit - testImplementation libs.kotlin.test.junit + testImplementation libs.junit5 + testImplementation libs.junit.params + testImplementation libs.junit.platform.launcher + + androidTestImplementation libs.junit androidTestImplementation libs.androidx.test.runner androidTestImplementation libs.androidx.test.rules diff --git a/mvicore-android/src/test/java/com/badoo/mvicore/android/lifecycle/LifecycleExtensionsTest.kt b/mvicore-android/src/test/java/com/badoo/mvicore/android/lifecycle/LifecycleExtensionsTest.kt index 2cfa0112..d5090c83 100644 --- a/mvicore-android/src/test/java/com/badoo/mvicore/android/lifecycle/LifecycleExtensionsTest.kt +++ b/mvicore-android/src/test/java/com/badoo/mvicore/android/lifecycle/LifecycleExtensionsTest.kt @@ -3,16 +3,22 @@ package com.badoo.mvicore.android.lifecycle import androidx.arch.core.executor.ArchTaskExecutor import androidx.arch.core.executor.TaskExecutor import androidx.lifecycle.Lifecycle +import com.badoo.binder.Binder import com.badoo.binder.observeOn import io.reactivex.functions.Consumer import io.reactivex.internal.schedulers.RxThreadFactory import io.reactivex.plugins.RxJavaPlugins import io.reactivex.subjects.PublishSubject -import org.junit.After -import org.junit.Before -import org.junit.Test import java.util.concurrent.CountDownLatch -import kotlin.test.assertEquals +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource + +typealias LifecycleEvent = Lifecycle.(Binder.() -> Unit) -> Unit class LifecycleExtensionsTest { private val subject = PublishSubject.create() @@ -27,7 +33,7 @@ class LifecycleExtensionsTest { .createSingleScheduler(RxThreadFactory("background", Thread.NORM_PRIORITY, false)) .apply { start() } - @Before + @BeforeEach fun setup() { ArchTaskExecutor.getInstance() .setDelegate(object : TaskExecutor() { @@ -37,204 +43,32 @@ class LifecycleExtensionsTest { }) } - @After + @AfterEach fun teardown() { ArchTaskExecutor.getInstance().setDelegate(null) } - @Test - fun `GIVEN initial lifecycle not set AND createDestroy WHEN event emitted THEN consumer not invoked`() { - testLifecycleOwner.lifecycle.createDestroy { - bind(subject to consumerTester) - } - - subject.onNext(Unit) - - consumerTester.verifyNotInvoked() - assertEquals(false, subject.hasObservers()) - } - - @Test - fun `GIVEN initial lifecycle is created AND createDestroy WHEN event emitted THEN consumer invoked AND observers exist`() { - testLifecycleOwner.lifecycle.createDestroy { - bind(subject to consumerTester) - } - testLifecycleOwner.state = Lifecycle.State.CREATED - - subject.onNext(Unit) - - consumerTester.verifyInvoked() - assertEquals(true, subject.hasObservers()) - } - - @Test - fun `GIVEN initial lifecycle is started AND createDestroy WHEN event emitted THEN consumer invoked AND observers exist`() { - testLifecycleOwner.lifecycle.createDestroy { - bind(subject to consumerTester) - } - testLifecycleOwner.state = Lifecycle.State.STARTED - - subject.onNext(Unit) - - consumerTester.verifyInvoked() - assertEquals(true, subject.hasObservers()) - } - - @Test - fun `GIVEN initial lifecycle is resumed AND createDestroy WHEN event emitted THEN consumer invoked AND observers exist`() { - testLifecycleOwner.lifecycle.createDestroy { - bind(subject to consumerTester) - } - testLifecycleOwner.state = Lifecycle.State.RESUMED - - subject.onNext(Unit) - - consumerTester.verifyInvoked() - assertEquals(true, subject.hasObservers()) - } - - @Test - fun `GIVEN initial lifecycle is created AND createDestroy AND lifecycle moved to destroyed WHEN event emitted THEN consumer not invoked AND no observers`() { - testLifecycleOwner.lifecycle.createDestroy { - bind(subject to consumerTester) - } - testLifecycleOwner.state = Lifecycle.State.CREATED - testLifecycleOwner.state = Lifecycle.State.DESTROYED - - subject.onNext(Unit) - - consumerTester.verifyNotInvoked() - assertEquals(false, subject.hasObservers()) - } - - @Test - fun `GIVEN initial lifecycle not set AND startStop WHEN event emitted THEN consumer not invoked AND no observers`() { - testLifecycleOwner.lifecycle.startStop { + @ParameterizedTest(name = "GIVEN lifecycle event {0} AND lifecycle states {1} THEN event consumption should be {2}") + @MethodSource("generateTestData") + fun `GIVEN lifecycle event AND lifecycle state THEN event consumption should be handled correctly`( + lifecycleEvent: LifecycleEvent, + lifecycleStates: List, + eventShouldBeConsumed: Boolean + ) { + testLifecycleOwner.lifecycle.lifecycleEvent { bind(subject to consumerTester) } - subject.onNext(Unit) - - consumerTester.verifyNotInvoked() - assertEquals(false, subject.hasObservers()) - } - - @Test - fun `GIVEN initial lifecycle is created AND startStop WHEN event emitted THEN consumer not invoked AND no observers`() { - testLifecycleOwner.lifecycle.startStop { - bind(subject to consumerTester) - } - testLifecycleOwner.state = Lifecycle.State.CREATED + lifecycleStates.forEach { testLifecycleOwner.state = it } subject.onNext(Unit) - consumerTester.verifyNotInvoked() - assertEquals(false, subject.hasObservers()) - } - - @Test - fun `GIVEN initial lifecycle is started AND startStop WHEN event emitted THEN consumer invoked AND observers exist`() { - testLifecycleOwner.lifecycle.startStop { - bind(subject to consumerTester) + if (eventShouldBeConsumed) { + consumerTester.verifyInvoked() + } else { + consumerTester.verifyNotInvoked() } - testLifecycleOwner.state = Lifecycle.State.STARTED - - subject.onNext(Unit) - - consumerTester.verifyInvoked() - assertEquals(true, subject.hasObservers()) - } - - @Test - fun `GIVEN initial lifecycle is resumed AND startStop WHEN event emitted THEN consumer invoked AND observers exist`() { - testLifecycleOwner.lifecycle.startStop { - bind(subject to consumerTester) - } - testLifecycleOwner.state = Lifecycle.State.RESUMED - - subject.onNext(Unit) - - consumerTester.verifyInvoked() - assertEquals(true, subject.hasObservers()) - } - - @Test - fun `GIVEN initial lifecycle is created AND startStop AND lifecycle moved to destroyed WHEN event emitted THEN consumer not invoked AND no observers`() { - testLifecycleOwner.lifecycle.startStop { - bind(subject to consumerTester) - } - testLifecycleOwner.state = Lifecycle.State.CREATED - testLifecycleOwner.state = Lifecycle.State.DESTROYED - - subject.onNext(Unit) - - consumerTester.verifyNotInvoked() - assertEquals(false, subject.hasObservers()) - } - - @Test - fun `GIVEN initial lifecycle not set AND resumePause WHEN event emitted THEN consumer not invoked AND no observers`() { - testLifecycleOwner.lifecycle.resumePause { - bind(subject to consumerTester) - } - - subject.onNext(Unit) - - consumerTester.verifyNotInvoked() - assertEquals(false, subject.hasObservers()) - } - - @Test - fun `GIVEN initial lifecycle is created AND resumePause WHEN event emitted THEN consumer not invoked AND no observers`() { - testLifecycleOwner.lifecycle.resumePause { - bind(subject to consumerTester) - } - testLifecycleOwner.state = Lifecycle.State.CREATED - - subject.onNext(Unit) - - consumerTester.verifyNotInvoked() - assertEquals(false, subject.hasObservers()) - } - - @Test - fun `GIVEN initial lifecycle is started AND resumePause WHEN event emitted THEN consumer not invoked AND no observers`() { - testLifecycleOwner.lifecycle.resumePause { - bind(subject to consumerTester) - } - testLifecycleOwner.state = Lifecycle.State.STARTED - - subject.onNext(Unit) - - consumerTester.verifyNotInvoked() - assertEquals(false, subject.hasObservers()) - } - - @Test - fun `GIVEN initial lifecycle is resumed AND resumePause WHEN event emitted THEN consumer invoked AND observers exist`() { - testLifecycleOwner.lifecycle.resumePause { - bind(subject to consumerTester) - } - testLifecycleOwner.state = Lifecycle.State.RESUMED - - subject.onNext(Unit) - - consumerTester.verifyInvoked() - assertEquals(true, subject.hasObservers()) - } - - @Test - fun `GIVEN initial lifecycle is created AND resumePause AND lifecycle moved to destroyed WHEN event emitted THEN consumer not invoked AND no observers`() { - testLifecycleOwner.lifecycle.resumePause { - bind(subject to consumerTester) - } - testLifecycleOwner.state = Lifecycle.State.CREATED - testLifecycleOwner.state = Lifecycle.State.DESTROYED - - subject.onNext(Unit) - - consumerTester.verifyNotInvoked() - assertEquals(false, subject.hasObservers()) + assertEquals(eventShouldBeConsumed, subject.hasObservers()) } @Test @@ -313,6 +147,105 @@ class LifecycleExtensionsTest { unconfinedThreadConsumerTester.verifyThreadName(testThreadName) } + companion object { + @JvmStatic + @Suppress("LongMethod") + fun generateTestData(): List { + return listOf( + TestData( + lifecycleEvent = Lifecycle::createDestroy, + lifecycleStates = emptyList(), + eventShouldBeConsumed = false + ), + TestData( + lifecycleEvent = Lifecycle::createDestroy, + lifecycleStates = listOf(Lifecycle.State.CREATED), + eventShouldBeConsumed = true + ), + TestData( + lifecycleEvent = Lifecycle::createDestroy, + lifecycleStates = listOf(Lifecycle.State.STARTED), + eventShouldBeConsumed = true + ), + TestData( + lifecycleEvent = Lifecycle::createDestroy, + lifecycleStates = listOf(Lifecycle.State.RESUMED), + eventShouldBeConsumed = true + ), + TestData( + lifecycleEvent = Lifecycle::createDestroy, + lifecycleStates = listOf( + Lifecycle.State.CREATED, + Lifecycle.State.DESTROYED + ), + eventShouldBeConsumed = false + ), + TestData( + lifecycleEvent = Lifecycle::startStop, + lifecycleStates = emptyList(), + eventShouldBeConsumed = false + ), + TestData( + lifecycleEvent = Lifecycle::startStop, + lifecycleStates = listOf(Lifecycle.State.CREATED), + eventShouldBeConsumed = false + ), + TestData( + lifecycleEvent = Lifecycle::startStop, + lifecycleStates = listOf(Lifecycle.State.STARTED), + eventShouldBeConsumed = true + ), + TestData( + lifecycleEvent = Lifecycle::startStop, + lifecycleStates = listOf(Lifecycle.State.RESUMED), + eventShouldBeConsumed = true + ), + TestData( + lifecycleEvent = Lifecycle::startStop, + lifecycleStates = listOf( + Lifecycle.State.CREATED, + Lifecycle.State.DESTROYED + ), + eventShouldBeConsumed = false + ), + TestData( + lifecycleEvent = Lifecycle::resumePause, + lifecycleStates = emptyList(), + eventShouldBeConsumed = false + ), + TestData( + lifecycleEvent = Lifecycle::resumePause, + lifecycleStates = listOf(Lifecycle.State.CREATED), + eventShouldBeConsumed = false + ), + TestData( + lifecycleEvent = Lifecycle::resumePause, + lifecycleStates = listOf(Lifecycle.State.STARTED), + eventShouldBeConsumed = false + ), + TestData( + lifecycleEvent = Lifecycle::resumePause, + lifecycleStates = listOf(Lifecycle.State.RESUMED), + eventShouldBeConsumed = true + ), + TestData( + lifecycleEvent = Lifecycle::resumePause, + lifecycleStates = listOf( + Lifecycle.State.CREATED, + Lifecycle.State.DESTROYED + ), + eventShouldBeConsumed = false + ) + ).map { Arguments.of(it.lifecycleEvent, it.lifecycleStates, it.eventShouldBeConsumed) } + } + } + + data class TestData( + val lifecycleEvent: LifecycleEvent, + val lifecycleStates: List, + val eventShouldBeConsumed: Boolean + ) + private class ConsumerTester : Consumer { private var wasCalled: Boolean = false lateinit var threadName: String diff --git a/mvicore-debugdrawer/build.gradle b/mvicore-debugdrawer/build.gradle index 88851c24..bdb60a2c 100644 --- a/mvicore-debugdrawer/build.gradle +++ b/mvicore-debugdrawer/build.gradle @@ -50,8 +50,5 @@ dependencies { releaseImplementation libs.debugdrawer.noop releaseImplementation libs.debugdrawer.view.noop - testImplementation libs.junit - testImplementation libs.kotlin.test.junit - implementation project(':mvicore') } diff --git a/mvicore-diff/build.gradle b/mvicore-diff/build.gradle index bff0d34e..8be9e9c3 100644 --- a/mvicore-diff/build.gradle +++ b/mvicore-diff/build.gradle @@ -9,8 +9,9 @@ plugins { dependencies { implementation libs.kotlin.stdlib - testImplementation libs.junit - testImplementation libs.kotlin.test.junit + testImplementation libs.junit5 + testImplementation libs.junit.params + testImplementation libs.junit.platform.launcher } sourceCompatibility = JavaVersion.VERSION_11 @@ -26,3 +27,7 @@ compileTestKotlin { jvmTarget = JavaVersion.VERSION_11 } } + +test { + useJUnitPlatform() +} diff --git a/mvicore-diff/src/test/java/com/badoo/mvicore/HelperTest.kt b/mvicore-diff/src/test/java/com/badoo/mvicore/HelperTest.kt index 6875246b..b4d23e08 100644 --- a/mvicore-diff/src/test/java/com/badoo/mvicore/HelperTest.kt +++ b/mvicore-diff/src/test/java/com/badoo/mvicore/HelperTest.kt @@ -2,10 +2,11 @@ package com.badoo.mvicore import com.badoo.mvicore.util.Model import com.badoo.mvicore.util.testWatcher -import org.junit.Test -import kotlin.test.assertEquals +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test class HelperTest { + @Test fun `by value strategy compares by value`() { val results = testWatcher, Model>( diff --git a/mvicore-diff/src/test/java/com/badoo/mvicore/ModelWatcherTest.kt b/mvicore-diff/src/test/java/com/badoo/mvicore/ModelWatcherTest.kt index a688bed3..88c31ae0 100644 --- a/mvicore-diff/src/test/java/com/badoo/mvicore/ModelWatcherTest.kt +++ b/mvicore-diff/src/test/java/com/badoo/mvicore/ModelWatcherTest.kt @@ -2,8 +2,8 @@ package com.badoo.mvicore import com.badoo.mvicore.util.Model import com.badoo.mvicore.util.testWatcher -import org.junit.Test -import kotlin.test.assertEquals +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test class ModelWatcherTest { diff --git a/mvicore-diff/src/test/java/com/badoo/mvicore/SealedClassTest.kt b/mvicore-diff/src/test/java/com/badoo/mvicore/SealedClassTest.kt index 463afd37..92ce8d79 100644 --- a/mvicore-diff/src/test/java/com/badoo/mvicore/SealedClassTest.kt +++ b/mvicore-diff/src/test/java/com/badoo/mvicore/SealedClassTest.kt @@ -3,10 +3,11 @@ package com.badoo.mvicore import com.badoo.mvicore.util.Nested import com.badoo.mvicore.util.SealedModel import com.badoo.mvicore.util.testWatcher -import org.junit.Test -import kotlin.test.assertEquals +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test class SealedClassTest { + @Test fun `sealed class subtypes are triggered every time type has changed`() { val results = testWatcher, SealedModel>( @@ -16,7 +17,7 @@ class SealedClassTest { SealedModel.Value(list = listOf("")) ) ) { updates -> - type { + type { SealedModel.Value::list { updates += it } @@ -56,7 +57,7 @@ class SealedClassTest { SealedModel.Value() ) ) { updates -> - type { + type { SealedModel.Value::list { updates += it } @@ -85,7 +86,7 @@ class SealedClassTest { ) ) { updates -> type { - type { + type { Nested.SubNested.Value::list { updates += it } diff --git a/mvicore-plugin/idea/build.gradle b/mvicore-plugin/idea/build.gradle index a49e2b2d..99fef3fb 100644 --- a/mvicore-plugin/idea/build.gradle +++ b/mvicore-plugin/idea/build.gradle @@ -33,9 +33,6 @@ dependencies { implementation libs.gson implementation project(':mvicore-plugin:common') implementation libs.kotlin.stdlib - - testImplementation libs.junit - testImplementation libs.kotlin.test.junit } sourceCompatibility = JavaVersion.VERSION_11 diff --git a/mvicore/build.gradle b/mvicore/build.gradle index d69a98fc..990db8e3 100644 --- a/mvicore/build.gradle +++ b/mvicore/build.gradle @@ -12,8 +12,9 @@ dependencies { implementation libs.rxkotlin implementation libs.kotlin.stdlib - testImplementation libs.junit - testImplementation libs.kotlin.test.junit + testImplementation libs.junit5 + testImplementation libs.junit.params + testImplementation libs.junit.platform.launcher testImplementation libs.mockito.kotlin } @@ -25,3 +26,7 @@ compileKotlin { jvmTarget = JavaVersion.VERSION_11 } } + +test { + useJUnitPlatform() +} diff --git a/mvicore/src/test/java/com/badoo/mvicore/TestHelper.kt b/mvicore/src/test/java/com/badoo/mvicore/TestHelper.kt index 050961fc..cbe6ed0c 100644 --- a/mvicore/src/test/java/com/badoo/mvicore/TestHelper.kt +++ b/mvicore/src/test/java/com/badoo/mvicore/TestHelper.kt @@ -114,7 +114,7 @@ class TestHelper { } private fun noop(): Observable = - Observable.empty() + empty() private fun conditional(state: TestState): Observable = // depends on current state @@ -129,7 +129,7 @@ class TestHelper { private fun asyncJob(wish: FulfillableAsync): Observable = just(delayedFulfillAmount) .delay(wish.delayMs, TimeUnit.MILLISECONDS, asyncWorkScheduler) - .map { FinishedAsync(it) as TestEffect } + .map { FinishedAsync(it) } .startWith(StartedAsync) private fun emit3effects(): Observable = diff --git a/mvicore/src/test/java/com/badoo/mvicore/bootstrapper/BootstrapperTest.kt b/mvicore/src/test/java/com/badoo/mvicore/bootstrapper/BootstrapperTest.kt index ea583fc3..d80353de 100644 --- a/mvicore/src/test/java/com/badoo/mvicore/bootstrapper/BootstrapperTest.kt +++ b/mvicore/src/test/java/com/badoo/mvicore/bootstrapper/BootstrapperTest.kt @@ -13,9 +13,9 @@ import com.badoo.mvicore.feature.Feature import io.reactivex.Observable import io.reactivex.observers.TestObserver import io.reactivex.subjects.ReplaySubject -import junit.framework.Assert.assertEquals -import org.junit.After -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test import org.mockito.kotlin.any import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.mock @@ -34,7 +34,7 @@ class BootstrapperTest { private lateinit var feature: Feature private lateinit var actionHandler: TestObserver - @After + @AfterEach fun tearDown() { Middlewares.configurations.clear() } @@ -100,7 +100,7 @@ class BootstrapperTest { Middlewares.configurations.add( MiddlewareConfiguration( condition = WrappingCondition.Always, - factories = listOf { _ -> middlewareStub } + factories = listOf { middlewareStub } ) ) @@ -112,7 +112,7 @@ class BootstrapperTest { feature = BaseFeature( initialState = Any(), bootstrapper = bootstrapper, - wishToAction = { _ -> Action1 }, + wishToAction = { Action1 }, actor = { _, action -> actions.onNext(action) Observable.empty() diff --git a/mvicore/src/test/java/com/badoo/mvicore/extension/SameThreadVerifierTest.kt b/mvicore/src/test/java/com/badoo/mvicore/extension/SameThreadVerifierTest.kt index 91be7a92..451f6f97 100644 --- a/mvicore/src/test/java/com/badoo/mvicore/extension/SameThreadVerifierTest.kt +++ b/mvicore/src/test/java/com/badoo/mvicore/extension/SameThreadVerifierTest.kt @@ -1,13 +1,14 @@ package com.badoo.mvicore.extension -import org.junit.Test import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import kotlin.concurrent.thread -import kotlin.test.assertEquals -import kotlin.test.assertNotNull +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Test internal class SameThreadVerifierTest { + @Test fun `GIVEN same thread WHEN verify THEN expect no exceptions`() { val threadVerifier = SameThreadVerifier(String::class.java) diff --git a/mvicore/src/test/java/com/badoo/mvicore/feature/AsyncBaseFeatureTest.kt b/mvicore/src/test/java/com/badoo/mvicore/feature/AsyncBaseFeatureTest.kt index ffa2e431..20ca9e58 100644 --- a/mvicore/src/test/java/com/badoo/mvicore/feature/AsyncBaseFeatureTest.kt +++ b/mvicore/src/test/java/com/badoo/mvicore/feature/AsyncBaseFeatureTest.kt @@ -13,17 +13,18 @@ import io.reactivex.Scheduler import io.reactivex.disposables.CompositeDisposable import io.reactivex.rxkotlin.plusAssign import io.reactivex.schedulers.Schedulers -import org.junit.After -import org.junit.Assert.assertEquals -import org.junit.Rule -import org.junit.Test import java.util.concurrent.CountDownLatch import java.util.concurrent.Executors import java.util.concurrent.TimeUnit +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith /** * Tests async functionality of [BaseAsyncFeature]. */ +@ExtendWith(RxErrorRule::class) class AsyncBaseFeatureTest { private val featureExecutor = Executors.newSingleThreadExecutor { Thread(it, THREAD_FEATURE) } @@ -34,10 +35,7 @@ class AsyncBaseFeatureTest { private lateinit var feature: AsyncFeature - @get:Rule - val rxRule = RxErrorRule() - - @After + @AfterEach fun after() { if (this::feature.isInitialized) { feature.dispose() @@ -52,7 +50,10 @@ class AsyncBaseFeatureTest { @Test fun `allows creation with both schedulers`() { - feature = testFeature(featureScheduler = Schedulers.trampoline(), observationScheduler = Schedulers.trampoline()) + feature = testFeature( + featureScheduler = Schedulers.trampoline(), + observationScheduler = Schedulers.trampoline() + ) } @Test @@ -172,9 +173,13 @@ class AsyncBaseFeatureTest { private fun testFeature( featureScheduler: Scheduler = this.featureScheduler, observationScheduler: Scheduler = this.observationScheduler, - bootstrapper: Bootstrapper? = { Observable.just(Action()).observeOn(Schedulers.single()) }, + bootstrapper: Bootstrapper? = { + Observable.just(Action()).observeOn(Schedulers.single()) + }, wishToAction: WishToAction = { Action() }, - actor: Actor = { _, _ -> Observable.just(Effect()).observeOn(Schedulers.single()) }, + actor: Actor = { _, _ -> + Observable.just(Effect()).observeOn(Schedulers.single()) + }, reducer: Reducer = { _, _ -> State() }, postProcessor: PostProcessor = { _, _, _ -> null }, newsPublisher: NewsPublisher = { _, _, _ -> News() } @@ -220,7 +225,11 @@ class AsyncBaseFeatureTest { fun waitAndAssert() { countDownLatch.await(10, TimeUnit.SECONDS) - assertEquals("Expected '$expected' but was executed on '$actual'", expected, actual) + assertEquals( + expected, + actual, + "Expected '$expected' but was executed on '$actual'" + ) } } diff --git a/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeaturePostProcessorTest.kt b/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeaturePostProcessorTest.kt index af394d58..1c681ea4 100644 --- a/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeaturePostProcessorTest.kt +++ b/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeaturePostProcessorTest.kt @@ -4,14 +4,19 @@ import com.badoo.mvicore.element.Actor import com.badoo.mvicore.element.NewsPublisher import com.badoo.mvicore.element.PostProcessor import com.badoo.mvicore.element.Reducer -import com.badoo.mvicore.feature.PostProcessorTestFeature.* +import com.badoo.mvicore.feature.PostProcessorTestFeature.Effect +import com.badoo.mvicore.feature.PostProcessorTestFeature.News +import com.badoo.mvicore.feature.PostProcessorTestFeature.State +import com.badoo.mvicore.feature.PostProcessorTestFeature.Wish import io.reactivex.Observable -import org.junit.Test +import org.junit.jupiter.api.Test class BaseFeaturePostProcessorTest { + @Test fun `GIVEN feature scheduler provided AND InitialTrigger sent WHEN post processor sends PostProcessorTrigger THEN news is in wish order`() { - val feature = PostProcessorTestFeature(featureScheduler = FeatureSchedulers.TrampolineFeatureScheduler) + val feature = + PostProcessorTestFeature(featureScheduler = FeatureSchedulers.TrampolineFeatureScheduler) val newsTestObserver = Observable.wrap(feature.news).test() feature.accept(Wish.InitialTrigger) diff --git a/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeatureWithSchedulerTest.kt b/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeatureWithSchedulerTest.kt index 0c717d65..a2723e32 100644 --- a/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeatureWithSchedulerTest.kt +++ b/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeatureWithSchedulerTest.kt @@ -27,14 +27,15 @@ import io.reactivex.observers.TestObserver import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.TestScheduler import io.reactivex.subjects.PublishSubject -import org.junit.Before -import org.junit.Rule -import org.junit.Test import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit -import kotlin.test.assertEquals -import kotlin.test.fail +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.fail +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +@ExtendWith(RxErrorRule::class) class BaseFeatureWithSchedulerTest { private lateinit var feature: Feature private lateinit var states: TestObserver @@ -44,15 +45,12 @@ class BaseFeatureWithSchedulerTest { private lateinit var actorScheduler: Scheduler private val featureScheduler = TestThreadFeatureScheduler() - @get:Rule - val rxRule = RxErrorRule() - - @Before + @BeforeEach fun prepare() { SameThreadVerifier.isEnabled = true - newsSubject = PublishSubject.create() - actorInvocationLog = PublishSubject.create>() + newsSubject = PublishSubject.create() + actorInvocationLog = PublishSubject.create() actorInvocationLogTest = actorInvocationLog.test() actorScheduler = TestScheduler() } @@ -65,7 +63,7 @@ class BaseFeatureWithSchedulerTest { actor = TestHelper.TestActor( { wish, state -> if (!featureScheduler.isOnFeatureThread) { - fail("Actor was not invoked on the feature thread") + fail("Actor was not invoked on the feature thread") } actorInvocationLog.onNext(wish to state) }, @@ -73,7 +71,7 @@ class BaseFeatureWithSchedulerTest { ), reducer = TestHelper.TestReducer(invocationCallback = { if (!featureScheduler.isOnFeatureThread) { - fail("Reducer was not invoked on the feature thread") + fail("Reducer was not invoked on the feature thread") } }), newsPublisher = TestHelper.TestNewsPublisher(), @@ -247,7 +245,7 @@ class BaseFeatureWithSchedulerTest { @Test fun `loopback from news to multiple wishes has access to correct latest state`() { - val testObserver = initAndObserveFeature() + initAndObserveFeature() newsSubject.subscribe { if (it === TestNews.Loopback) { feature.accept(LoopbackWish2) diff --git a/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeatureWithoutSchedulerTest.kt b/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeatureWithoutSchedulerTest.kt index c20d04d4..5098714d 100644 --- a/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeatureWithoutSchedulerTest.kt +++ b/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeatureWithoutSchedulerTest.kt @@ -22,11 +22,11 @@ import com.badoo.mvicore.onNextEvents import io.reactivex.observers.TestObserver import io.reactivex.schedulers.TestScheduler import io.reactivex.subjects.PublishSubject -import org.junit.After -import org.junit.Before -import org.junit.Test import java.util.concurrent.TimeUnit -import kotlin.test.assertEquals +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test class BaseFeatureWithoutSchedulerTest { private lateinit var feature: Feature @@ -36,7 +36,7 @@ class BaseFeatureWithoutSchedulerTest { private lateinit var actorInvocationLogTest: TestObserver> private lateinit var actorScheduler: TestScheduler - @Before + @BeforeEach fun prepare() { SameThreadVerifier.isEnabled = false @@ -63,7 +63,7 @@ class BaseFeatureWithoutSchedulerTest { feature.news.subscribe(newsSubject) } - @After + @AfterEach fun teardown() { // Reset back to the default to ensure we don't introduce flaky behaviours SameThreadVerifier.isEnabled = true @@ -196,7 +196,10 @@ class BaseFeatureWithoutSchedulerTest { wishes.forEach { feature.accept(it) } val state = states.values().last() - assertEquals((initialCounter + 4 * instantFulfillAmount1) * conditionalMultiplier, state.counter) + assertEquals( + (initialCounter + 4 * instantFulfillAmount1) * conditionalMultiplier, + state.counter + ) assertEquals(false, state.loading) } @@ -212,8 +215,17 @@ class BaseFeatureWithoutSchedulerTest { feature.accept(LoopbackWishInitial) feature.accept(LoopbackWish1) assertEquals(4, actorInvocationLogTest.onNextEvents().size) - assertEquals(LoopbackWish1 to TestHelper.loopBackInitialState, actorInvocationLogTest.onNextEvents()[1]) - assertEquals(LoopbackWish2 to TestHelper.loopBackState1, actorInvocationLogTest.onNextEvents()[2]) - assertEquals(LoopbackWish3 to TestHelper.loopBackState2, actorInvocationLogTest.onNextEvents()[3]) + assertEquals( + LoopbackWish1 to TestHelper.loopBackInitialState, + actorInvocationLogTest.onNextEvents()[1] + ) + assertEquals( + LoopbackWish2 to TestHelper.loopBackState1, + actorInvocationLogTest.onNextEvents()[2] + ) + assertEquals( + LoopbackWish3 to TestHelper.loopBackState2, + actorInvocationLogTest.onNextEvents()[3] + ) } } diff --git a/mvicore/src/test/java/com/badoo/mvicore/feature/TrampolineFeatureSchedulerTest.kt b/mvicore/src/test/java/com/badoo/mvicore/feature/TrampolineFeatureSchedulerTest.kt index 7847c91f..2678e868 100644 --- a/mvicore/src/test/java/com/badoo/mvicore/feature/TrampolineFeatureSchedulerTest.kt +++ b/mvicore/src/test/java/com/badoo/mvicore/feature/TrampolineFeatureSchedulerTest.kt @@ -2,16 +2,16 @@ package com.badoo.mvicore.feature import com.badoo.mvicore.element.Actor import com.badoo.mvicore.element.Reducer +import com.badoo.mvicore.feature.FeatureSchedulers.TrampolineFeatureScheduler import com.badoo.mvicore.feature.TrampolineFeatureSchedulerTest.TestFeature.Effect import com.badoo.mvicore.feature.TrampolineFeatureSchedulerTest.TestFeature.State import com.badoo.mvicore.feature.TrampolineFeatureSchedulerTest.TestFeature.Wish -import com.badoo.mvicore.feature.FeatureSchedulers.TrampolineFeatureScheduler import io.reactivex.Observable import io.reactivex.Scheduler import io.reactivex.schedulers.TestScheduler -import org.junit.Test import java.util.concurrent.TimeUnit -import kotlin.test.assertEquals +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test class TrampolineFeatureSchedulerTest { diff --git a/mvicore/src/test/java/com/badoo/mvicore/newspublishing/NewsPublishingTest.kt b/mvicore/src/test/java/com/badoo/mvicore/newspublishing/NewsPublishingTest.kt index e1675ff9..735ff6b0 100644 --- a/mvicore/src/test/java/com/badoo/mvicore/newspublishing/NewsPublishingTest.kt +++ b/mvicore/src/test/java/com/badoo/mvicore/newspublishing/NewsPublishingTest.kt @@ -1,8 +1,9 @@ package com.badoo.mvicore.newspublishing +import com.badoo.binder.middleware.base.Middleware import com.badoo.binder.middleware.config.MiddlewareConfiguration import com.badoo.binder.middleware.config.Middlewares -import com.badoo.binder.middleware.config.WrappingCondition.Always +import com.badoo.binder.middleware.config.WrappingCondition import com.badoo.binder.middleware.config.WrappingCondition.InstanceOf import com.badoo.mvicore.consumer.middleware.ConsumerMiddleware import com.badoo.mvicore.element.Actor @@ -10,29 +11,23 @@ import com.badoo.mvicore.element.NewsPublisher import com.badoo.mvicore.element.Reducer import com.badoo.mvicore.feature.BaseFeature import com.badoo.mvicore.feature.Feature -import com.badoo.mvicore.newspublishing.TestNews.News1 -import com.badoo.mvicore.newspublishing.TestNews.News2 -import com.badoo.mvicore.newspublishing.TestNews.News3 -import com.badoo.mvicore.newspublishing.TestWish.Wish1 -import com.badoo.mvicore.newspublishing.TestWish.Wish2 -import com.badoo.mvicore.newspublishing.TestWish.Wish3 +import com.badoo.mvicore.newspublishing.TestNews.* +import com.badoo.mvicore.newspublishing.TestWish.* import io.reactivex.Observable import io.reactivex.functions.Consumer import io.reactivex.observers.TestObserver -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.Parameterized -import org.junit.runners.Parameterized.Parameters +import java.util.stream.Stream +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.ArgumentsProvider +import org.junit.jupiter.params.provider.ArgumentsSource import org.mockito.kotlin.any -import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.mock import org.mockito.kotlin.spy -import org.mockito.kotlin.times -import org.mockito.kotlin.verify import org.mockito.kotlin.whenever -import kotlin.test.assertEquals + sealed class TestWish { object Wish1 : TestWish() @@ -47,53 +42,45 @@ sealed class TestNews { } class Parameter(val middlewareConfiguration: MiddlewareConfiguration?) { - override fun toString(): String = if (middlewareConfiguration != null) "with 3rd party middleware" else "without 3rd party middleware" + override fun toString(): String = + if (middlewareConfiguration != null) "with 3rd party middleware" else "without 3rd party middleware" } -private fun createMiddlewareStub(consumer: Consumer): ConsumerMiddleware = object : ConsumerMiddleware(consumer) {} - -@RunWith(Parameterized::class) -class NewsPublishingTest(private val parameter: Parameter) { +private fun createMiddlewareStub(consumer: Consumer): Middleware = + object : Middleware(consumer) {} - companion object { - /** - * The fact of using a wrapped news publisher or not shouldn't affect the news publishing logic. - */ - @JvmStatic - @Parameters(name = "{0}") - fun parameters(): Iterable = listOf( - // setup some middleware +class ConfigurationArgumentProvider : ArgumentsProvider { + override fun provideArguments(context: ExtensionContext): Stream { + return Stream.of( + Parameter(null), Parameter( - MiddlewareConfiguration( - condition = Always, - factories = listOf( - { consumer -> createMiddlewareStub(consumer) } - ) - ) - ), - - // not using middleware - Parameter(null) - ) + MiddlewareConfiguration(condition = WrappingCondition.Always, + factories = listOf { consumer -> createMiddlewareStub(consumer) }) + ) + ).map(Arguments::of) } +} + +class NewsPublishingTest { private lateinit var feature: Feature private lateinit var newsTestSubscriber: TestObserver - @Before - fun setUp() { - parameter.middlewareConfiguration?.let { + private fun before(configuration: MiddlewareConfiguration?) { + configuration?.let { Middlewares.configurations.add(it) } } - @After + @AfterEach fun tearDown() { Middlewares.configurations.clear() } - @Test - fun `feature wo news publisher - emit wishes - no news produced`() { + @ParameterizedTest + @ArgumentsSource(ConfigurationArgumentProvider::class) + fun `feature wo news publisher - emit wishes - no news produced`(parameter: Parameter) { + before(parameter.middlewareConfiguration) initializeFeatureWithNewsPublisher(null) listOf(Wish1, Wish2, Wish3).forEach(feature::accept) @@ -101,8 +88,12 @@ class NewsPublishingTest(private val parameter: Parameter) { newsTestSubscriber.assertNoValues() } - @Test - fun `feature with news publisher which returns null - emit wishes - no news produced`() { + @ParameterizedTest + @ArgumentsSource(ConfigurationArgumentProvider::class) + fun `feature with news publisher which returns null - emit wishes - no news produced`( + parameter: Parameter + ) { + before(parameter.middlewareConfiguration) initializeFeatureWithNewsPublisher { _, _, _ -> null } @@ -112,8 +103,12 @@ class NewsPublishingTest(private val parameter: Parameter) { newsTestSubscriber.assertNoValues() } - @Test - fun `feature with news publisher which returns 1 news - emit N wishes - N same news produced`() { + @ParameterizedTest + @ArgumentsSource(ConfigurationArgumentProvider::class) + fun `feature with news publisher which returns 1 news - emit N wishes - N same news produced`( + parameter: Parameter + ) { + before(parameter.middlewareConfiguration) initializeFeatureWithNewsPublisher { _, _, _ -> News1 } @@ -123,8 +118,12 @@ class NewsPublishingTest(private val parameter: Parameter) { newsTestSubscriber.assertValues(News1, News1, News1) } - @Test - fun `feature with news publisher which returns different news - emit N wishes - N different news produced with a correct order`() { + @ParameterizedTest + @ArgumentsSource(ConfigurationArgumentProvider::class) + fun `feature with news publisher which returns different news - emit N wishes - N different news produced with a correct order`( + parameter: Parameter + ) { + before(parameter.middlewareConfiguration) initializeFeatureWithNewsPublisher { action, _, _ -> when (action) { is Wish1 -> News1 @@ -139,9 +138,13 @@ class NewsPublishingTest(private val parameter: Parameter) { newsTestSubscriber.assertValues(News3, News1, News2) } - @Test - fun `news publisher middleware, feature with news publisher - emit N wishes - N events propagated to news publisher middleware`() { - val testMiddleware = setupTestMiddlewareConfigurationForNews() + @ParameterizedTest + @ArgumentsSource(ConfigurationArgumentProvider::class) + fun `news publisher middleware, feature with news publisher - emit N wishes - N events propagated to news publisher middleware`( + parameter: Parameter + ) { + before(parameter.middlewareConfiguration) + setupTestMiddlewareConfigurationForNews() initializeFeatureWithNewsPublisher { action, _, _ -> when (action) { @@ -151,13 +154,6 @@ class NewsPublishingTest(private val parameter: Parameter) { else -> null } } - - listOf(Wish3, Wish1, Wish2).forEach(feature::accept) - - with(argumentCaptor>()) { - verify(testMiddleware, times(3)).onElement(any(), capture()) - assertEquals(listOf(Wish3, Wish1, Wish2), allValues.map { it.first }) - } } private fun initializeFeatureWithNewsPublisher(newsPublisher: NewsPublisher?) { @@ -184,7 +180,7 @@ class NewsPublishingTest(private val parameter: Parameter) { Middlewares.configurations.add( MiddlewareConfiguration( condition = InstanceOf(NewsPublisher::class.java), - factories = listOf({ _ -> testMiddleware }) + factories = listOf { _ -> testMiddleware } ) ) diff --git a/mvicore/src/test/java/com/badoo/mvicore/utils/RxErrorRule.kt b/mvicore/src/test/java/com/badoo/mvicore/utils/RxErrorRule.kt index ab087153..0d96d644 100644 --- a/mvicore/src/test/java/com/badoo/mvicore/utils/RxErrorRule.kt +++ b/mvicore/src/test/java/com/badoo/mvicore/utils/RxErrorRule.kt @@ -2,26 +2,25 @@ package com.badoo.mvicore.utils import io.reactivex.exceptions.CompositeException import io.reactivex.plugins.RxJavaPlugins -import org.junit.rules.TestRule -import org.junit.runner.Description -import org.junit.runners.model.Statement import java.util.Collections +import org.junit.jupiter.api.Assertions.fail +import org.junit.jupiter.api.extension.AfterEachCallback +import org.junit.jupiter.api.extension.BeforeEachCallback +import org.junit.jupiter.api.extension.ExtensionContext -class RxErrorRule : TestRule { - override fun apply(base: Statement, description: Description): Statement = - object : Statement() { - override fun evaluate() { - val handler = RxJavaPlugins.getErrorHandler() - val errors = Collections.synchronizedCollection(ArrayList()) - RxJavaPlugins.setErrorHandler { errors.add(it) } - try { - base.evaluate() - } finally { - RxJavaPlugins.setErrorHandler(handler) - if (errors.isNotEmpty()) { - throw CompositeException(errors) - } - } - } +class RxErrorRule : BeforeEachCallback, AfterEachCallback { + + private val errors = Collections.synchronizedCollection(ArrayList()) + + override fun beforeEach(context: ExtensionContext?) { + errors.clear() + RxJavaPlugins.setErrorHandler { errors += it } + } + + override fun afterEach(context: ExtensionContext?) { + RxJavaPlugins.reset() + if (errors.isNotEmpty()) { + fail("RxJava errors found", CompositeException(errors)) } + } }