fork

fun fork(name: String = "", from: String? = null, block: AggregateSpec<S>.() -> Unit)

Runs block on a copy of the aggregate as it is at the ref from (ref), or now when from is null, so that several scenarios share a prefix: what block does stays in the fork, and the specification goes on from where it was. A failure in the fork names it (name, or from).

The fork starts on a copy of everything the specification holds: the aggregate's log, its given state, its timers, the reservations, the ids counted so far and the time. Nothing before the fork runs again, so a domain service the decide functions called before it is not called again, however many forks there are. In a tree of tests (aggregateSpecTests) the fork is a container named by it.

aggregateSpec(orderModel) {
whenCommand(PlaceOrder(...)) { expectEventTypes(OrderPlaced::class) }
fork("paid") { whenCommand(PayOrder(...)) { expectEventTypes(OrderPaid::class) } }
fork("cancelled") { whenCommand(CancelOrder(...)) { expectEventTypes(OrderCancelled::class) } }
}

fun fork(name: String = "", from: String? = null, model: AggregateModel<S>, block: AggregateSpec<S>.() -> Unit)

Runs block on a copy of the aggregate as fork does, with model deciding from there on: the same aggregate built with other services, as a domain module's generated factory builds it (orderModel(name, OutOfStockInventory)), so that one branch of a scenario sees a service answer otherwise. The copy keeps the log, which model reads as its own, so model is of the same aggregate type; the services the commands before the fork called are not called again.