Skip to main content

FakeMediator & Test Helpers

mediatork-test ships a set of test helpers that let you write handler and ViewModel tests without a mocking library.

HelperWhat it does
FakeMediatorReal mediator backed by a live HandlerRegistry. Register handlers at any time.
DummyMediatorZero-arg no-op. send silently returns, publish does nothing.
fakeHandlerCreates a RequestHandler from a suspend lambda.
fakeNotificationHandlerCreates a NotificationHandler from a suspend lambda.
captureNotificationsRegisters a notification handler and returns the live captured list.

DummyMediator

Use DummyMediator when a test needs a Mediator instance to satisfy a constructor but never actually calls send.

@Test
fun `initial state is empty and not loading`() {
val vm = OrderViewModel(DummyMediator())
assertEquals(OrderUiState(), vm.stateFlow.value)
}

send returns Unit silently — no exception, no result processing. If your test does call send and the result matters, use FakeMediator instead.


FakeMediator

FakeMediator wraps a real HandlerRegistry and a real mediator pipeline. It dispatches requests to whatever handlers you register, giving you the full pipeline (behaviors, pre/post processors) without a running application.

Register handlers at construction

val mediator = FakeMediator {
+CreateOrderHandler()
+FetchUserHandler()
}

Register handlers after construction

Handlers can also be added mid-test — useful for changing behaviour between calls in the same test:

@Test
fun `error is cleared on next call`() = runTest {
val mediator = FakeMediator()
val vm = OrderViewModel(mediator)

mediator.register(fakeHandler<CreateOrderCommand, OrderResult> { _, _, _ ->
throw RuntimeException("first failure")
})
vm.createOrder("1", 10.0)
advanceUntilIdle()
assertNotNull(vm.stateFlow.value.error)

mediator.register(fakeHandler<CreateOrderCommand, OrderResult> { _, _, _ ->
OrderResult(orderId = "ORD-2")
})
vm.createOrder("2", 20.0)
advanceUntilIdle()

assertNull(vm.stateFlow.value.error)
assertEquals("ORD-2", vm.stateFlow.value.orderResult?.orderId)
}

Calling register again for the same request type silently replaces the previous handler.

With registrars and pipeline behaviors

val mediator = FakeMediator(
registrars = listOf(OrderRegistrar()),
pipelineBehaviors = listOf(LoggingBehavior()),
)

fakeHandler

fakeHandler builds a RequestHandler from a suspend lambda. The type arguments pin the request and result types — no anonymous object boilerplate.

@Test
fun `createOrder returns expected result`() = runTest {
val mediator = FakeMediator()
val vm = OrderViewModel(mediator)

mediator.register(fakeHandler<CreateOrderCommand, OrderResult> { _, _, request ->
OrderResult(orderId = request.id)
})

vm.createOrder("ORD-1", 99.0)
advanceUntilIdle()

assertEquals("ORD-1", vm.stateFlow.value.orderResult?.orderId)
}

Throw from the lambda to simulate failures:

@Test
fun `createOrder failure sets error`() = runTest {
val mediator = FakeMediator()
val vm = OrderViewModel(mediator)

mediator.register(fakeHandler<CreateOrderCommand, OrderResult> { _, _, _ ->
throw RuntimeException("Network unavailable")
})

vm.createOrder("1", 99.0)
advanceUntilIdle()

assertEquals("Network unavailable", vm.stateFlow.value.error)
}

Choosing the right helper

SituationUse
Test only checks initial state, never calls sendDummyMediator()
Test controls what send returnsFakeMediator + fakeHandler
Test captures published notificationscaptureNotifications
Test verifies all handlers are wired upMediatorTestUtils.assertAllHandlersRegistered