diff --git a/formula-test/src/main/java/com/instacart/formula/test/RxJavaFormulaTestDelegate.kt b/formula-test/src/main/java/com/instacart/formula/test/RxJavaFormulaTestDelegate.kt
index 0692ea290..b4fdc4112 100644
--- a/formula-test/src/main/java/com/instacart/formula/test/RxJavaFormulaTestDelegate.kt
+++ b/formula-test/src/main/java/com/instacart/formula/test/RxJavaFormulaTestDelegate.kt
@@ -13,8 +13,8 @@ import io.reactivex.rxjava3.subjects.BehaviorSubject
class RxJavaFormulaTestDelegate>(
override val formula: FormulaT,
isValidationEnabled: Boolean = true,
- inspector: Inspector? = null,
- dispatcher: Dispatcher? = null,
+ inspector: Inspector?,
+ dispatcher: Dispatcher?,
) : FormulaTestDelegate {
private val runtimeConfig = RuntimeConfig(
isValidationEnabled = isValidationEnabled,
diff --git a/formula-test/src/main/java/com/instacart/formula/test/TestActionObserver.kt b/formula-test/src/main/java/com/instacart/formula/test/TestActionObserver.kt
index 857545eda..5a6a1f0ac 100644
--- a/formula-test/src/main/java/com/instacart/formula/test/TestActionObserver.kt
+++ b/formula-test/src/main/java/com/instacart/formula/test/TestActionObserver.kt
@@ -27,6 +27,10 @@ class TestActionObserver(private val action: Action) {
* provide a [Cancelable].
*/
fun cancel() {
- cancelation!!.cancel()
+ val cancelable = cancelation ?: run {
+ throw IllegalStateException("Action did not return a cancelable.")
+ }
+
+ cancelable.cancel()
}
}
diff --git a/formula-test/src/main/java/com/instacart/formula/test/TestCallback.kt b/formula-test/src/main/java/com/instacart/formula/test/TestCallback.kt
index 5d4895d00..3699aee2e 100644
--- a/formula-test/src/main/java/com/instacart/formula/test/TestCallback.kt
+++ b/formula-test/src/main/java/com/instacart/formula/test/TestCallback.kt
@@ -8,8 +8,8 @@ class TestCallback : () -> Unit {
}
fun assertTimesCalled(times: Int) {
- assert(invocationCount == times) {
- "Expected: $times, was: $invocationCount"
+ if (invocationCount != times) {
+ throw AssertionError("Expected: $times, was: $invocationCount")
}
}
}
diff --git a/formula-test/src/main/java/com/instacart/formula/test/TestFormulaObserver.kt b/formula-test/src/main/java/com/instacart/formula/test/TestFormulaObserver.kt
index 8958d8373..cbaa2ce99 100644
--- a/formula-test/src/main/java/com/instacart/formula/test/TestFormulaObserver.kt
+++ b/formula-test/src/main/java/com/instacart/formula/test/TestFormulaObserver.kt
@@ -28,7 +28,7 @@ class TestFormulaObserver Unit) = apply {
+ fun output(assert: Output.() -> Unit) = apply {
ensureFormulaIsRunning()
assertNoErrors() // Check before interaction
assert(values().last())
@@ -39,8 +39,9 @@ class TestFormulaObserver : Listener {
fun assertTimesCalled(times: Int) {
val timesCalled = values.size
- assert(timesCalled == times) {
- "Expected: $times, was: $timesCalled"
+ if (timesCalled != times) {
+ throw AssertionError("Expected: $times, was: $timesCalled")
}
}
}
diff --git a/formula-test/src/main/java/com/instacart/formula/test/TestRuntimeExtensions.kt b/formula-test/src/main/java/com/instacart/formula/test/TestRuntimeExtensions.kt
index ab0049342..44222f5c2 100644
--- a/formula-test/src/main/java/com/instacart/formula/test/TestRuntimeExtensions.kt
+++ b/formula-test/src/main/java/com/instacart/formula/test/TestRuntimeExtensions.kt
@@ -19,21 +19,5 @@ fun > F.test(
return TestFormulaObserver(delegate)
}
-/**
- * An extension function to create a [TestFormulaObserver] for a [IFormula] instance.
- *
- * @param initialInput Input passed to [IFormula].
- */
-fun > F.test(
- initialInput: Input,
- isValidationEnabled: Boolean = true,
- inspector: Inspector? = null,
- dispatcher: Dispatcher? = null,
-): TestFormulaObserver {
- return test(isValidationEnabled, inspector, dispatcher).apply {
- input(initialInput)
- }
-}
-
fun Action.test() = TestActionObserver(this)
diff --git a/formula-test/src/test/java/com/instacart/formula/test/CountingInspectorTest.kt b/formula-test/src/test/java/com/instacart/formula/test/CountingInspectorTest.kt
new file mode 100644
index 000000000..a2d77b987
--- /dev/null
+++ b/formula-test/src/test/java/com/instacart/formula/test/CountingInspectorTest.kt
@@ -0,0 +1,75 @@
+package com.instacart.formula.test
+
+import com.google.common.truth.Truth
+import com.instacart.formula.Evaluation
+import com.instacart.formula.Snapshot
+import com.instacart.formula.StatelessFormula
+import org.junit.Test
+
+class CountingInspectorTest {
+
+ @Test
+ fun `assertEvaluationCount throws exception when count does not match`() {
+ val inspector = CountingInspector()
+ inspector.assertEvaluationCount(0)
+
+ val result = runCatching { inspector.assertEvaluationCount(5) }
+ Truth.assertThat(result.exceptionOrNull()).hasMessageThat().contains(
+ "Evaluation count does not match - count: 0, expected: 5"
+ )
+ }
+
+ @Test
+ fun `assertEvaluationCount with types throws exception when count does not match`() {
+ val inspector = CountingInspector()
+ inspector.assertEvaluationCount(MyFormula::class, 0)
+ inspector.onEvaluateFinished(MyFormula::class, null, true)
+ inspector.assertEvaluationCount(MyFormula::class, 1)
+
+ val result = runCatching { inspector.assertEvaluationCount(MyFormula::class, 5) }
+ Truth.assertThat(result.exceptionOrNull()).hasMessageThat().contains(
+ "Evaluation count does not match - count: 1, expected: 5"
+ )
+ }
+
+ @Test
+ fun `assertRunCount throws exception when count does not match`() {
+ val inspector = CountingInspector()
+ inspector.assertRunCount(0)
+
+ val result = runCatching { inspector.assertRunCount(5) }
+ Truth.assertThat(result.exceptionOrNull()).hasMessageThat().contains(
+ "Run count does not match - count: 0, expected: 5"
+ )
+ }
+
+ @Test
+ fun `assertActionsStarted throws exception when count does not match`() {
+ val inspector = CountingInspector()
+ inspector.assertActionsStarted(0)
+
+ val result = runCatching { inspector.assertActionsStarted(5) }
+ Truth.assertThat(result.exceptionOrNull()).hasMessageThat().contains(
+ "Actions started count does not match - count: 0, expected: 5"
+ )
+ }
+
+ @Test
+ fun `assertStateTransitions throws exception when count does not match`() {
+ val inspector = CountingInspector()
+ inspector.assertStateTransitions(MyFormula::class, 0)
+ inspector.onStateChanged(MyFormula::class, null, null, null)
+ inspector.assertStateTransitions(MyFormula::class, 1)
+
+ val result = runCatching { inspector.assertStateTransitions(MyFormula::class, 5) }
+ Truth.assertThat(result.exceptionOrNull()).hasMessageThat().contains(
+ "State transition count does not match - count: 1, expected: 5"
+ )
+ }
+
+ class MyFormula : StatelessFormula() {
+ override fun Snapshot.evaluate(): Evaluation {
+ return Evaluation(Unit)
+ }
+ }
+}
\ No newline at end of file
diff --git a/formula-test/src/test/java/com/instacart/formula/test/TestActionObserverTest.kt b/formula-test/src/test/java/com/instacart/formula/test/TestActionObserverTest.kt
new file mode 100644
index 000000000..d08cfc8f8
--- /dev/null
+++ b/formula-test/src/test/java/com/instacart/formula/test/TestActionObserverTest.kt
@@ -0,0 +1,61 @@
+package com.instacart.formula.test
+
+import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
+import com.instacart.formula.Action
+import com.instacart.formula.Cancelable
+import org.junit.Test
+import java.lang.IllegalStateException
+
+class TestActionObserverTest {
+
+ @Test fun `assert values success`() {
+ multipleValueStream().test().assertValues(1, 2)
+ }
+
+ @Test fun `assert value fails due to different size`() {
+ val result = runCatching { multipleValueStream().test().assertValues(1) }
+ assertThat(result.exceptionOrNull()).isInstanceOf(AssertionError::class.java)
+ }
+
+ @Test fun `assert value fails due to different value`() {
+ val result = runCatching { multipleValueStream().test().assertValues(1, 5) }
+ assertThat(result.exceptionOrNull()).isInstanceOf(AssertionError::class.java)
+ }
+
+ @Test fun values() {
+ val values = multipleValueStream().test().values()
+ assertThat(values).hasSize(2)
+ }
+
+ @Test fun `cancel throws exception if action does not provide cancelable`() {
+ val result = kotlin.runCatching { multipleValueStream().test().cancel() }
+ assertThat(result.exceptionOrNull()).hasMessageThat().contains(
+ "Action did not return a cancelable."
+ )
+ }
+
+ @Test fun `cancel invokes cancelable`() {
+ var cancelableCalled = 0
+ val action = object : Action {
+ override fun start(send: (String) -> Unit): Cancelable {
+ return Cancelable { cancelableCalled += 1 }
+ }
+
+ override fun key(): Any? = null
+ }
+
+ action.test().cancel()
+ assertThat(cancelableCalled).isEqualTo(1)
+ }
+
+ private fun multipleValueStream() = object : Action {
+ override fun start(send: (Int) -> Unit): Cancelable? {
+ send(1)
+ send(2)
+ return null
+ }
+
+ override fun key(): Any = Unit
+ }
+}
diff --git a/formula-test/src/test/java/com/instacart/formula/test/TestCallbackTest.kt b/formula-test/src/test/java/com/instacart/formula/test/TestCallbackTest.kt
new file mode 100644
index 000000000..a59ff61c0
--- /dev/null
+++ b/formula-test/src/test/java/com/instacart/formula/test/TestCallbackTest.kt
@@ -0,0 +1,20 @@
+package com.instacart.formula.test
+
+import com.google.common.truth.Truth
+import org.junit.Test
+
+class TestCallbackTest {
+
+ @Test
+ fun `assertTimesCalled throws an exception when count does not match`() {
+ val callback = TestCallback()
+ callback.assertTimesCalled(0)
+ callback.invoke()
+ callback.assertTimesCalled(1)
+
+ val result = kotlin.runCatching { callback.assertTimesCalled(5) }
+ Truth.assertThat(result.exceptionOrNull()).hasMessageThat().contains(
+ "Expected: 5, was: 1"
+ )
+ }
+}
\ No newline at end of file
diff --git a/formula-test/src/test/java/com/instacart/formula/test/TestFormulaObserverTest.kt b/formula-test/src/test/java/com/instacart/formula/test/TestFormulaObserverTest.kt
new file mode 100644
index 000000000..9b5385f1b
--- /dev/null
+++ b/formula-test/src/test/java/com/instacart/formula/test/TestFormulaObserverTest.kt
@@ -0,0 +1,58 @@
+package com.instacart.formula.test
+
+import com.google.common.truth.Truth
+import com.instacart.formula.Evaluation
+import com.instacart.formula.Snapshot
+import com.instacart.formula.StatelessFormula
+import org.junit.Test
+
+class TestFormulaObserverTest {
+
+ @Test fun `assertOutput passes if count matches`() {
+ val formula = object : StatelessFormula() {
+ override fun Snapshot.evaluate(): Evaluation {
+ return Evaluation(input)
+ }
+ }
+
+ val observer = formula.test()
+ observer.input(1)
+ observer.assertOutputCount(1)
+
+ observer.input(10)
+ observer.assertOutputCount(2)
+ }
+
+ @Test fun `assertOutput throws exception if count does not match`() {
+ val formula = object : StatelessFormula() {
+ override fun Snapshot.evaluate(): Evaluation {
+ return Evaluation(input)
+ }
+ }
+
+ val observer = formula.test()
+ observer.input(1)
+ val result = kotlin.runCatching {
+ observer.assertOutputCount(5)
+ }
+
+ Truth.assertThat(result.exceptionOrNull()).hasMessageThat().contains(
+ "Expected: 5, was: 1"
+ )
+ }
+
+ @Test fun `output throws error if formula is not running`() {
+ val formula = object : StatelessFormula() {
+ override fun Snapshot.evaluate(): Evaluation {
+ return Evaluation(input)
+ }
+ }
+
+ val result = runCatching {
+ formula.test().output { }
+ }
+ Truth.assertThat(result.exceptionOrNull()).hasMessageThat().contains(
+ "Formula is not running. Call [TestFormulaObserver.input] to start it."
+ )
+ }
+}
\ No newline at end of file
diff --git a/formula-test/src/test/java/com/instacart/formula/test/TestFormulaTest.kt b/formula-test/src/test/java/com/instacart/formula/test/TestFormulaTest.kt
index 7c4a511b1..b48469b06 100644
--- a/formula-test/src/test/java/com/instacart/formula/test/TestFormulaTest.kt
+++ b/formula-test/src/test/java/com/instacart/formula/test/TestFormulaTest.kt
@@ -1,6 +1,5 @@
package com.instacart.formula.test
-import com.google.common.truth.Truth
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.fail
import org.junit.Test
@@ -9,8 +8,24 @@ class TestFormulaTest {
@Test fun `assert running count is zero when formula is not running`() {
val formula = TestSimpleFormula()
formula.implementation.assertRunningCount(0)
- formula.test().input(SimpleFormula.Input())
+
+ val observer = formula.test()
+ observer.input(SimpleFormula.Input())
formula.implementation.assertRunningCount(1)
+
+ observer.dispose()
+ formula.implementation.assertRunningCount(0)
+ }
+
+ @Test
+ fun `assert running count throws an exception when count does not match`() {
+ val formula = TestSimpleFormula()
+ val result = runCatching {
+ formula.implementation.assertRunningCount(5)
+ }
+ assertThat(result.exceptionOrNull()).hasMessageThat().contains(
+ "Expected 5 running formulas, but there were 0 instead"
+ )
}
@Test fun `emits initial output when subscribed`() {
diff --git a/formula-test/src/test/java/com/instacart/formula/test/TestListenerTest.kt b/formula-test/src/test/java/com/instacart/formula/test/TestListenerTest.kt
new file mode 100644
index 000000000..6ba3bc5fb
--- /dev/null
+++ b/formula-test/src/test/java/com/instacart/formula/test/TestListenerTest.kt
@@ -0,0 +1,34 @@
+package com.instacart.formula.test
+
+import com.google.common.truth.Truth
+import org.junit.Test
+
+class TestListenerTest {
+
+ @Test
+ fun `assertTimesCalled throws an exception when count does not match`() {
+ val listener = TestListener()
+ listener.assertTimesCalled(0)
+ listener.invoke("value")
+ listener.assertTimesCalled(1)
+ listener.invoke("second")
+ listener.assertTimesCalled(2)
+
+ val result = runCatching { listener.assertTimesCalled(5) }
+ Truth.assertThat(result.exceptionOrNull()).hasMessageThat().contains(
+ "Expected: 5, was: 2"
+ )
+ }
+
+ @Test
+ fun values() {
+ val listener = TestListener()
+ listener.invoke("value")
+ listener.invoke("second")
+ listener.invoke("third")
+
+ Truth.assertThat(listener.values()).containsExactly(
+ "value", "second", "third"
+ ).inOrder()
+ }
+}
\ No newline at end of file
diff --git a/formula-test/src/test/java/com/instacart/formula/test/TestStreamTest.kt b/formula-test/src/test/java/com/instacart/formula/test/TestStreamTest.kt
deleted file mode 100644
index f1bf66740..000000000
--- a/formula-test/src/test/java/com/instacart/formula/test/TestStreamTest.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.instacart.formula.test
-
-import com.google.common.truth.Truth.assertThat
-import com.instacart.formula.Action
-import com.instacart.formula.Cancelable
-import org.junit.Test
-import java.lang.IllegalStateException
-
-class TestStreamTest {
-
- @Test fun `assert values success`() {
- multipleValueStream().test().assertValues(1, 2)
- }
-
- @Test fun `assert value fails due to different size`() {
- val exception = fails { multipleValueStream().test().assertValues(1) }
- assertThat(exception).isInstanceOf(AssertionError::class.java)
- }
-
- @Test fun `assert value fails due to different value`() {
- val exception = fails { multipleValueStream().test().assertValues(1, 5) }
- assertThat(exception).isInstanceOf(AssertionError::class.java)
- }
-
- inline fun fails(action: () -> Unit): Throwable {
- try {
- action()
- } catch (t: Error) {
- return t
- }
-
- throw IllegalStateException("Action succeeded.")
- }
-
- fun multipleValueStream() = object : Action {
- override fun start(send: (Int) -> Unit): Cancelable? {
- send(1)
- send(2)
- return null
- }
-
- override fun key(): Any = Unit
- }
-}
diff --git a/samples/counter/src/test/java/com/instacart/formula/counter/CounterFormulaTest.kt b/samples/counter/src/test/java/com/instacart/formula/counter/CounterFormulaTest.kt
index af2f1230e..3b54fbe46 100644
--- a/samples/counter/src/test/java/com/instacart/formula/counter/CounterFormulaTest.kt
+++ b/samples/counter/src/test/java/com/instacart/formula/counter/CounterFormulaTest.kt
@@ -9,7 +9,8 @@ class CounterFormulaTest {
@Test fun `increment 5 times`() {
CounterFormula()
- .test(Unit)
+ .test()
+ .input(Unit)
.output { onIncrement(Unit) }
.output { onIncrement(Unit) }
.output { onIncrement(Unit) }
diff --git a/samples/custom-network-state-stream/src/test/java/com/instacart/formula/stopwatch/NetworkStateFormulaTest.kt b/samples/custom-network-state-stream/src/test/java/com/instacart/formula/stopwatch/NetworkStateFormulaTest.kt
index c4a014eb9..9f4f02033 100644
--- a/samples/custom-network-state-stream/src/test/java/com/instacart/formula/stopwatch/NetworkStateFormulaTest.kt
+++ b/samples/custom-network-state-stream/src/test/java/com/instacart/formula/stopwatch/NetworkStateFormulaTest.kt
@@ -12,7 +12,8 @@ class NetworkStateFormulaTest {
@Test fun offline() {
formula(isOnline = false)
- .test(Unit)
+ .test()
+ .input(Unit)
.output {
assertThat(status).isEqualTo("Network state: OFFLINE")
}
@@ -20,7 +21,8 @@ class NetworkStateFormulaTest {
@Test fun connected() {
formula(isOnline = true)
- .test(Unit)
+ .test()
+ .input(Unit)
.output {
assertThat(status).isEqualTo("Network state: CONNECTED")
}
diff --git a/samples/stopwatch/src/test/java/com/instacart/formula/stopwatch/StopwatchFormulaTest.kt b/samples/stopwatch/src/test/java/com/instacart/formula/stopwatch/StopwatchFormulaTest.kt
index e6c7e7ded..4d5cac294 100644
--- a/samples/stopwatch/src/test/java/com/instacart/formula/stopwatch/StopwatchFormulaTest.kt
+++ b/samples/stopwatch/src/test/java/com/instacart/formula/stopwatch/StopwatchFormulaTest.kt
@@ -7,6 +7,6 @@ class StopwatchFormulaTest {
// TODO:
@Test fun `increment 5 times`() {
- StopwatchFormula().test(Unit)
+ StopwatchFormula().test().input(Unit)
}
}
diff --git a/samples/todoapp/src/test/java/com/examples/todoapp/tasks/TaskListFormulaTest.kt b/samples/todoapp/src/test/java/com/examples/todoapp/tasks/TaskListFormulaTest.kt
index f1fa8eec5..cfc72120e 100644
--- a/samples/todoapp/src/test/java/com/examples/todoapp/tasks/TaskListFormulaTest.kt
+++ b/samples/todoapp/src/test/java/com/examples/todoapp/tasks/TaskListFormulaTest.kt
@@ -18,7 +18,8 @@ class TaskListFormulaTest {
val repo = TaskRepoFake()
TaskListFormula(repo)
- .test(TaskListFormula.Input(showToast = showToast))
+ .test()
+ .input(TaskListFormula.Input(showToast = showToast))
.output {
assertThat(items).hasSize(2)
}