| name | workflow-testing |
| description | Write unit tests for StatefulWorkflow and StatelessWorkflow using testRender and RenderTester. Use for workflow unit testing, render testing, expectWorker, expectWorkflow, action verification, or WorkflowOutput assertions. |
Workflow Unit Testing with testRender
Write unit tests for individual render passes using testRender and RenderTester. This API
fakes all children and workers, letting you test render logic in isolation.
When to Use
- Testing a single render pass in isolation
- Verifying renderings match expected UI models
- Testing state transitions from event handlers
- Testing output emissions
- Verifying props passed to child workflows
- Verifying worker expectations
For multi-step flows or async behavior, use renderForTest / WorkflowTurbine instead
(see the workflow-integration-testing skill).
Test File Structure
import com.squareup.workflow1.WorkflowOutput
import com.squareup.workflow1.testing.expectWorker
import com.squareup.workflow1.testing.expectWorkflow
import com.squareup.workflow1.testing.testRender
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull
class MyWorkflowTest {
private val workflow = MyWorkflow()
@Test fun `renders initial state correctly`() {
workflow.testRender(
props = MyProps("test"),
initialState = MyState.Initial
)
.render { rendering ->
assertEquals("test", rendering.title)
assertEquals(false, rendering.isLoading)
}
}
}
Core API
Starting a Test
workflow.testRender(props = myProps, initialState = myState)
workflow.testRender(props = myProps)
workflow.testRender(props = myProps)
Rendering and Asserting
workflow.testRender(props, state)
.render { rendering ->
assertEquals("Hello", rendering.title)
assertTrue(rendering.isLoading)
rendering.onButtonClicked()
}
Verifying Actions
After triggering an event handler or receiving a child/worker output, verify the action:
.verifyAction { action ->
assertEquals(MyAction.LoadData, action)
}
.verifyActionResult { newState, output ->
assertEquals(MyState.Loading, newState)
assertNull(output)
}
output is WorkflowOutput<OutputT>? — use output?.value to access the actual value,
or check assertNull(output) when no output is expected.
Testing Patterns
State Transitions
@Test fun `button click transitions to loading`() {
workflow.testRender(
props = MyProps("test"),
initialState = MyState.Initial
)
.render { rendering ->
rendering.onLoadClicked()
}
.verifyActionResult { newState, output ->
assertEquals(MyState.Loading, newState)
assertNull(output)
}
}
Output Emissions
@Test fun `complete button emits finished output`() {
workflow.testRender(
props = MyProps("test"),
initialState = MyState.Done("result")
)
.render { rendering ->
rendering.onCompleteClicked()
}
.verifyActionResult { newState, output ->
assertEquals(MyOutput.Finished("result"), output?.value)
}
}
No Action Triggered
@Test fun `renders loading state without triggering action`() {
workflow.testRender(
props = MyProps("test"),
initialState = MyState.Loading
)
.render { rendering ->
assertTrue(rendering.isLoading)
assertEquals("Loading...", rendering.message)
}
}
Expecting Child Workflows
All child workflows rendered by the workflow-under-test must be faked via providing expectations:
@Test fun `renders child workflow with correct props`() {
workflow.testRender(props = MyProps("123"), initialState = MyState.ShowChild)
.expectWorkflow(
workflowType = ChildWorkflow::class,
rendering = ChildScreen("faked"),
key = "",
assertProps = { props ->
assertEquals("123", props.itemId)
}
)
.render { rendering ->
assertEquals("faked", rendering.childContent)
}
}
Child Workflow Emitting Output
@Test fun `handles child workflow output`() {
workflow.testRender(props = MyProps("123"), initialState = MyState.ShowChild)
.expectWorkflow(
workflowType = ChildWorkflow::class,
rendering = ChildScreen("faked"),
output = WorkflowOutput(ChildOutput.Done("result"))
)
.render { rendering ->
}
.verifyActionResult { newState, output ->
assertEquals(MyState.Complete("result"), newState)
}
}
Faking Workers
Workers are optionally expected by default. Use requireExplicitWorkerExpectations() to
make all workers required.
By Worker Type (KType)
import kotlin.reflect.typeOf
workflow.testRender(props, state)
.expectWorker(
workerType = typeOf<Worker<MyData>>(),
key = "fetchData"
)
.render { ... }
By Worker Class (KClass)
workflow.testRender(props, state)
.expectWorker(
workerClass = MyCustomWorker::class,
key = "fetch"
)
.render { ... }
By Output Type
workflow.testRender(props, state)
.expectWorkerOutputting(
outputType = typeOf<MyData>(),
key = "fetchData"
)
.render { ... }
Worker Emitting Output
workflow.testRender(props, state)
.expectWorker(
workerType = typeOf<Worker<MyData>>(),
key = "fetchData",
output = WorkflowOutput(MyData("result"))
)
.render { rendering ->
}
.verifyActionResult { newState, output ->
assertEquals(MyState.Loaded("result"), newState)
}
Side Effects
workflow.testRender(props, state)
.expectSideEffect(key = "analytics")
.render { ... }
Side effects are optionally expected by default.
Use requireExplicitSideEffectExpectations() to require all side effects be expected.
Chaining Render Passes
Test multiple sequential renders without the overhead of a full runtime:
@Test fun `multi-step flow`() {
workflow.testRender(props = MyProps("test"), initialState = MyState.Initial)
.render { rendering ->
rendering.onLoadClicked()
}
.verifyActionResult { newState, _ ->
assertEquals(MyState.Loading, newState)
}
.testNextRender()
.expectWorker(workerType = typeOf<Worker<Data>>(), key = "fetch")
.render { rendering ->
assertTrue(rendering.isLoading)
}
}
With New Props
.testNextRenderWithProps(MyProps("updated"))
.render { rendering ->
assertEquals("updated", rendering.title)
}
Note: testNextRenderWithProps will call onPropsChanged if the workflow overrides it.
Best Practices
- Test one behavior per test — each test should verify one event handler or one render state
- Use
verifyActionResult for inline/anonymous actions (most common)
- Use
verifyAction for sealed class or enum actions where you test the action type
- Use
requireExplicitWorkerExpectations() when you need to verify no unexpected workers run
- Descriptive test names with backticks:
`button click transitions to loading`
- Only trigger one event per render — triggering an event AND having a child/worker emit
output in the same render is an error
Required Imports
import com.squareup.workflow1.testing.testRender
import com.squareup.workflow1.testing.expectWorkflow
import com.squareup.workflow1.testing.expectWorker
import com.squareup.workflow1.testing.expectWorkerOutputting
import com.squareup.workflow1.testing.expectSideEffect
import com.squareup.workflow1.WorkflowOutput
import kotlin.reflect.typeOf
import com.squareup.workflow1.Worker
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull
import kotlin.test.assertTrue
Deprecated APIs — Do NOT Use
launchForTestingFromStartWith — replaced by renderForTest
launchForTestingWith — replaced by renderForTest
WorkflowTestRuntime — replaced by WorkflowTurbine
These are deprecated integration test APIs. For integration testing, use the
workflow-integration-testing skill instead.
Documentation