RenderTester

The props must be specified, the initial state may be specified, and then all child workflows and workers that are expected to run, and any outputs from them, must be specified with expectWorkflow and (optionally) expectWorker and expectSideEffect calls. If one needs to verify all workers explicitly, perhaps to verify that a worker is not run, then use requireExplicitWorkerExpectations. Likewise requireExplicitSideEffectExpectations for side effects. Then call render and perform any assertions on the rendering. An event may also be sent to the rendering if no workflows or workers emitted an output. Lastly, the RenderTestResult returned by render may be used to assert on the WorkflowActions processed to handle events or outputs by calling verifyAction or verifyActionResult.

  • All workflows that are rendered/ran by this workflow must be specified.

  • Workers are optionally specified. Specified workers must run. Unexpected workers on a render pass do not cause a test failure unless requireExplicitWorkerExpectations is true. Side effects are optionally specified. Specified side effects must run. Unexpected side effects on a render pass do not cause a test failure unless requireExplicitSideEffectExpectations is true.

  • It is an error if more than one workflow or worker specifies an output.

  • It is a test failure if any workflows or workers that were expected were not ran.

  • It is a test failure if the workflow tried to run any workflows that were not expected.

  • It is a test failure if no workflow or worker emitted an output, no rendering event was invoked, and any of the action verification methods on RenderTestResult is called.

Examples

Worker output

The following example tests a render pass that runs one worker, SubmitLoginWorker, which is configured to have "emitted" an output, and one workflow, ChildWorkflow, which expects a props containing "test@foo.com" and returning a ChildRendering as its rendering.

It checks that the rendering properties are expected and that the output handler for the SubmitLoginWorker returned the CompleteLogin action.

workflow
.testRender(
props = MyProps(…),
initialState = MyState(…)
)
.expectWorker(
workerClass = SubmitLoginWorker::class
key = "signin",
output = WorkflowOutput(LoginResponse(success = true))
)
.expectWorkflow(
workflowType = ChildWorkflow::class,
key = "child",
assertProps = { assertThat(it.email).isEqualTo("test@foo.com") },
rendering = ChildRendering("message")
)
.render { rendering ->
assertThat(rendering.text).isEqualTo("foo")
}
.verifyAction { action ->
assertThat(action).isEqualTo(Action.CompleteLogin(success = true))
}

Rendering event

This is similar to the example above, but will test an event sent to the rendering instead.

workflow
.testRender(
props = MyProps(…),
initialState = MyState(…)
)
.expectWorker(
matchesWhen = { it is SubmitLoginWorker },
key = "signin"
)
.expectWorkflow(
workflowType = ChildWorkflow::class,
key = "child",
assertProps = { assertThat(it.email).isEqualTo("test@foo.com") },
rendering = ChildRendering("message")
)
.render { rendering ->
rendering.onCancelClicked()
assertThat(rendering.text).isEqualTo("foo")
}
.verifyAction { action ->
assertThat(action).isEqualTo(Action.CancelLogin)
}

Verify action result

This test verifies the action result instead of the action itself. This technique is useful if the WorkflowAction is anonymous or inline.

val currentState = …
val previousState = …

workflow
.testRender(
props = MyProps(…),
initialState = currentState
)
.render { rendering ->
rendering.onCancelClicked()
}
.verifyActionResult { newState, output ->
// Check that the workflow navigated back correctly.
assertThat(newState).isEqualTo(previousState)

// Check that the workflow didn't emit any output from the button click.
assertThat(output).isNull()
}

Too many outputs

This is an example of what not to do – this test will error out because a worker is emitting and output and a rendering event is sent.

workflow
.testRender(
props = MyProps(…),
initialState = MyState(…)
)
.expectWorker(
matchesWhen = { it is SubmitLoginWorker },
key = "signin",
output = WorkflowOutput(LoginResponse(success = true))
)
.expectWorkflow(
workflowType = ChildWorkflow::class,
key = "child",
assertProps = { assertThat(it.email).isEqualTo("test@foo.com") },
rendering = ChildRendering("message")
)
.render { rendering ->
// This will throw and fail the test because the SubmitLoginWorker is also configured to emit
// an output.
rendering.onCancelClicked()

Constructors

Link copied to clipboard
constructor()

Types

Link copied to clipboard
sealed class ChildWorkflowMatch
Link copied to clipboard
class RenderChildInvocation(val workflow: Workflow<*, *, *>, val props: Any?, val outputType: KTypeProjection, val renderingType: KTypeProjection, val renderKey: String)

Describes a call to RenderContext.renderChild.

Functions

Link copied to clipboard
abstract fun expectSideEffect(description: String, exactMatch: Boolean = true, matcher: (key: String) -> Boolean): RenderTester<PropsT, StateT, OutputT, RenderingT>

Specifies that this render pass is expected to run a side effect with a key that satisfies matcher. This expectation is strict, and will fail if multiple side effects match.

Link copied to clipboard

Specifies that this render pass is expected to run a particular side effect.

Link copied to clipboard

Specifies that this render pass is expected to run a Worker that has the same type of the given worker and for which the actual worker's doesSameWorkAs method returns true. If a worker is ran that matches the type of expected, but the actual worker's doesSameWorkAs returns false, then an AssertionError will be thrown. If you need to perform custom assertions, use the overload of this method that takes an assertWhen parameter.

inline fun <PropsT, StateT, OutputT, RenderingT, WorkerOutputT, WorkerT : Worker<WorkerOutputT>> RenderTester<PropsT, StateT, OutputT, RenderingT>.expectWorker(workerClass: KClass<out WorkerT>, key: String = "", crossinline assertWorker: (WorkerT) -> Unit = {}, output: WorkflowOutput<WorkerOutputT>? = null, description: String = ""): RenderTester<PropsT, StateT, OutputT, RenderingT>

Specifies that this render pass is expected to run a Worker with the given workerClass. The worker's output type is not taken into consideration.

fun <PropsT, StateT, OutputT, RenderingT> RenderTester<PropsT, StateT, OutputT, RenderingT>.expectWorker(workerType: KType, key: String = "", assertWorker: (Worker<*>) -> Unit = {}, output: WorkflowOutput<*>? = null, description: String = ""): RenderTester<PropsT, StateT, OutputT, RenderingT>

Specifies that this render pass is expected to run a Worker whose KType matches workerType.

Link copied to clipboard
inline fun <PropsT, StateT, OutputT, RenderingT> RenderTester<PropsT, StateT, OutputT, RenderingT>.expectWorkerOutputting(outputType: KType, key: String = "", crossinline assertWorker: (Worker<*>) -> Unit = {}, output: WorkflowOutput<*>? = null, description: String = ""): RenderTester<PropsT, StateT, OutputT, RenderingT>

Specifies that this render pass is expected to run a Worker with the given outputType.

Link copied to clipboard
inline fun <ChildRenderingT, PropsT, StateT, OutputT, RenderingT> RenderTester<PropsT, StateT, OutputT, RenderingT>.expectWorkflow(identifier: WorkflowIdentifier, rendering: ChildRenderingT, key: String = "", description: String = "", noinline assertProps: (props: Any?) -> Unit = {}): RenderTester<PropsT, StateT, OutputT, RenderingT>

Specifies that this render pass is expected to render a particular child workflow.

Link copied to clipboard
abstract fun render(block: (rendering: RenderingT) -> Unit = {}): RenderTestResult<PropsT, StateT, OutputT, RenderingT>

Execute the workflow's render method and run block to perform assertions on and send events to the resulting rendering.