Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions workflow-testing/api/workflow-testing.api
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,7 @@ public final class com/squareup/workflow/testing/RenderIdempotencyChecker : com/

public abstract interface class com/squareup/workflow/testing/RenderTestResult {
public abstract fun verifyAction (Lkotlin/jvm/functions/Function1;)V
public abstract fun verifyActionOutput (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow/testing/RenderTestResult;
public abstract fun verifyActionResult (Lkotlin/jvm/functions/Function2;)V
public abstract fun verifyActionState (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow/testing/RenderTestResult;
public abstract fun verifyNoActionOutput ()Lcom/squareup/workflow/testing/RenderTestResult;
}

public final class com/squareup/workflow/testing/RenderTestResult$DefaultImpls {
public static fun verifyActionResult (Lcom/squareup/workflow/testing/RenderTestResult;Lkotlin/jvm/functions/Function2;)V
}

public abstract interface class com/squareup/workflow/testing/RenderTester {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,30 +202,10 @@ internal class RealRenderTester<PropsT, StateT, OutputT, RenderingT>(
block(action)
}

override fun verifyActionState(block: (newState: StateT) -> Unit) = apply {
verifyAction { action ->
// Don't care about output.
val (newState, _) = action.applyTo(state)
block(newState)
}
}

override fun verifyActionOutput(block: (output: OutputT) -> Unit) = apply {
verifyAction { action ->
val (_, output) = action.applyTo(state)
if (output == null) {
throw AssertionError("Expected action to set an output")
}
block(output.value)
}
}

override fun verifyNoActionOutput() = apply {
verifyAction { action ->
val (_, output) = action.applyTo(state)
if (output != null) {
throw AssertionError("Expected no output, but action set output to: ${output.value}")
}
override fun verifyActionResult(block: (newState: StateT, output: WorkflowOutput<OutputT>?) -> Unit) {
verifyAction {
val (state, output) = it.applyTo(state)
block(state, output)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.squareup.workflow.testing

import com.squareup.workflow.WorkflowAction
import com.squareup.workflow.WorkflowOutput

/**
* Result of a [RenderTester.render] call that can be used to verify that a [WorkflowAction] was
Expand All @@ -37,46 +38,6 @@ interface RenderTestResult<StateT, OutputT> {
*/
fun verifyAction(block: (WorkflowAction<StateT, OutputT>) -> Unit)

/**
* If the render pass handled either a workflow/worker output or a rendering event, "executes" the
* action with the state passed to [testRender], then invokes [block] with the resulting state
* value.
*
* If the workflow didn't process any actions, `newState` will be the initial state.
*
* Note that by using this method, you're also testing the implementation of your action. This can
* be useful if your actions are anonymous. If they are a sealed class or enum, use [verifyAction]
* instead and write separate unit tests for your action implementations.
*/
fun verifyActionState(block: (newState: StateT) -> Unit): RenderTestResult<StateT, OutputT>

/**
* If the render pass handled either a workflow/worker output or a rendering event, "executes" the
* action with the state passed to [testRender], verifies that the action set an output, then
* invokes [block] with the resulting output value.
*
* If the workflow didn't process any actions, or no output was set, an [AssertionError] will be
* thrown.
*
* Note that by using this method, you're also testing the implementation of your action. This can
* be useful if your actions are anonymous. If they are a sealed class or enum, use [verifyAction]
* instead and write separate unit tests for your action implementations.
*/
fun verifyActionOutput(block: (output: OutputT) -> Unit): RenderTestResult<StateT, OutputT>

/**
* If the render pass handled either a workflow/worker output or a rendering event, "executes" the
* action with the state passed to [testRender], and then verifies that the action did not set
* any output.
*
* If the workflow didn't process any actions, this method will do nothing.
*
* Note that by using this method, you're also testing the implementation of your action. This can
* be useful if your actions are anonymous. If they are a sealed class or enum, use [verifyAction]
* instead and write separate unit tests for your action implementations.
*/
fun verifyNoActionOutput(): RenderTestResult<StateT, OutputT>

/**
* Asserts that the render pass handled either a workflow/worker output or a rendering event,
* "executes" the action with the state passed to [testRender], then invokes [block] with the
Expand All @@ -88,23 +49,6 @@ interface RenderTestResult<StateT, OutputT> {
* Note that by using this method, you're also testing the implementation of your action. This can
* be useful if your actions are anonymous. If they are a sealed class or enum, use [verifyAction]
* instead and write separate unit tests for your action implementations.
*
* Note that if [OutputT] is nullable, this method does not distinguish between an no output and
* null output. Use [RenderTestResult.verifyActionOutput] and
* [RenderTestResult.verifyNoActionOutput] instead.
*/
@Deprecated("Use verifyActionState and verify(No)ActionOutput")
fun verifyActionResult(block: (newState: StateT, output: OutputT?) -> Unit) {
var state: StateT? = null
var output: OutputT? = null
verifyActionState { state = it }
try {
verifyActionOutput { output = it }
} catch (e: AssertionError) {
// No output was set, leave as null.
}

@Suppress("UNCHECKED_CAST")
block(state as StateT, output)
}
fun verifyActionResult(block: (newState: StateT, output: WorkflowOutput<OutputT>?) -> Unit)
}
Original file line number Diff line number Diff line change
Expand Up @@ -673,8 +673,7 @@ class RealRenderTesterTest {
}
}

@Suppress("DEPRECATION")
@Test fun `verifyAction and verifyActionResult pass when no action processed`() {
@Test fun `verifyAction allows no action`() {
val workflow = Workflow.stateless<Unit, Nothing, Sink<TestAction>> {
actionSink.contraMap { it }
}
Expand All @@ -690,74 +689,7 @@ class RealRenderTesterTest {
}
}

@Suppress("DEPRECATION")
@Test fun `verifyActionResult works`() {
class TestAction : WorkflowAction<String, String> {
override fun Updater<String, String>.apply() {
state = "new state"
setOutput("output")
}
}

val workflow = Workflow.stateful<Unit, String, String, Sink<TestAction>>(
initialState = { "initial" },
render = { _, _ -> actionSink.contraMap { it } }
)
val testResult = workflow.testRender(Unit)
.render { sink ->
sink.send(TestAction())
}

testResult.verifyActionResult { state, output ->
assertEquals("new state", state)
assertEquals("output", output)
}
}

@Test fun `verifyActionState and verifyActionOutput chain`() {
class TestAction : WorkflowAction<String, String> {
override fun Updater<String, String>.apply() {
state = "new state"
setOutput("output")
}
}

val workflow = Workflow.stateful<Unit, String, String, Sink<TestAction>>(
initialState = { "initial" },
render = { _, _ -> actionSink.contraMap { it } }
)
val testResult = workflow.testRender(Unit)
.render { sink ->
sink.send(TestAction())
}

testResult
.verifyActionState { assertEquals("new state", it) }
.verifyActionOutput { assertEquals("output", it) }
}

@Test fun `verifyActionState and verifyNoActionOutput chain`() {
class TestAction : WorkflowAction<String, String> {
override fun Updater<String, String>.apply() {
state = "new state"
}
}

val workflow = Workflow.stateful<Unit, String, String, Sink<TestAction>>(
initialState = { "initial" },
render = { _, _ -> actionSink.contraMap { it } }
)
val testResult = workflow.testRender(Unit)
.render { sink ->
sink.send(TestAction())
}

testResult
.verifyActionState { assertEquals("new state", it) }
.verifyNoActionOutput()
}

@Test fun `verifyActionState allows no action`() {
@Test fun `verifyActionResult allows no action`() {
val workflow = Workflow.stateless<Unit, Nothing, Sink<TestAction>> {
actionSink.contraMap { it }
}
Expand All @@ -766,52 +698,16 @@ class RealRenderTesterTest {
// Don't send to sink!
}

testResult.verifyAction { assertEquals(noAction(), it) }
testResult.verifyActionState { newState ->
testResult.verifyActionResult { newState, output ->
assertSame(Unit, newState)
assertNull(output)
}
}

@Test fun `verifyActionState handles new state`() {
@Test fun `verifyActionResult handles new state and output`() {
class TestAction : WorkflowAction<String, String> {
override fun Updater<String, String>.apply() {
state = "new state"
}
}

val workflow = Workflow.stateful<Unit, String, String, Sink<TestAction>>(
initialState = { "initial" },
render = { _, _ -> actionSink.contraMap { it } }
)
val testResult = workflow.testRender(Unit)
.render { sink ->
sink.send(TestAction())
}

testResult.verifyActionState { state ->
assertEquals("new state", state)
}
}

@Test fun `verifyActionOutput allows no action`() {
val workflow = Workflow.stateless<Unit, Nothing, Sink<TestAction>> {
actionSink.contraMap { it }
}
val testResult = workflow.testRender(Unit)
.render {
// Don't send to sink!
}

testResult.verifyAction { assertEquals(noAction(), it) }
val error = assertFailsWith<AssertionError> {
testResult.verifyActionOutput {}
}
assertEquals("Expected action to set an output", error.message)
}

@Test fun `verifyActionOutput handles output`() {
class TestAction : WorkflowAction<String, String> {
override fun Updater<String, String>.apply() {
setOutput("output")
}
}
Expand All @@ -825,44 +721,10 @@ class RealRenderTesterTest {
sink.send(TestAction())
}

testResult.verifyActionOutput { output ->
assertEquals("output", output)
}
}

@Test fun `verifyNoActionOutput allows no action`() {
val workflow = Workflow.stateless<Unit, Nothing, Sink<TestAction>> {
actionSink.contraMap { it }
}
val testResult = workflow.testRender(Unit)
.render {
// Don't send to sink!
}

testResult.verifyAction { assertEquals(noAction(), it) }
testResult.verifyNoActionOutput()
}

@Test fun `verifyNoActionOutput fails when output is set`() {
class TestAction : WorkflowAction<String, String> {
override fun Updater<String, String>.apply() {
setOutput("output")
}
}

val workflow = Workflow.stateful<Unit, String, String, Sink<TestAction>>(
initialState = { "initial" },
render = { _, _ -> actionSink.contraMap { it } }
)
val testResult = workflow.testRender(Unit)
.render { sink ->
sink.send(TestAction())
}

val error = assertFailsWith<AssertionError> {
testResult.verifyNoActionOutput()
testResult.verifyActionResult { state, output ->
assertEquals("new state", state)
assertEquals("output", output?.value)
}
assertEquals("Expected no output, but action set output to: output", error.message)
}

@Test fun `render is executed multiple times`() {
Expand Down