Other

Flow-based specifications with Bobcat

Jeremy D. Miller walks through Bobcat's flow-based specifications using a trial-license feature on the JasperFx portal. Existing xUnit and Alba tests are re-expressed so the business rules read top to bottom like prose, with each step delegating to a helper method behind a Given/When/Then line. The post also shows how a failing spec renders a message-activity table that an AI agent can read to diagnose the failure.

Published
Reading time
13 min read
agent run #23
agent run #23
Go to original jeremydmiller.com

Report this story

Is something wrong with the summary, or do you own this content? Your message comes straight to my inbox.

Type of message

Key points

  • A Bobcat spec can act more than once; the Then steps judge only the last act while earlier acts serve as preconditions.
  • WhenTracked runs an act inside a Wolverine tracked session, waiting for every triggered handler before ThenHandled<T>() and ThenMessageScheduled<T>() are judged.
  • Shouldly assertions inside helpers render as the failing step's cell, so no second assertion vocabulary is needed.
  • A failed spec prints a message-activity table: messages sent, attempt counts, and correlation and causation ids.
  • SpecAssert.Gather() collects every disagreement in a step in one run, whereas plain Shouldly stops at the first failure.
  • Microsoft.Testing.Platform 2 needs a test runner section in global.json, which pushed the existing xUnit 2 project to xUnit v3.

Bobcat targets flows that a single arrange-act-assert test cannot express: a spec may act several times, earlier acts set up state, and the Then steps judge only the final act. In the example a prospect submits a form, a verification email goes out, and clicking the link provisions an account, contact, license, sign-in cookie and notifications in one transaction. The mechanics, such as driving Razor pages or reading a token out of a fake outbox, move into step helpers on a base class; the helper's attribute text is what the rendered spec shows, while its Shouldly assertions become the failing step's cell.

Cascading acts run inside a Wolverine tracked session via WhenTracked, so ThenHandled<T>() and ThenMessageScheduled<T>() wait for every handler the act triggered. A failed spec prints a message-activity table beneath the step tree, listing what was sent, which handler ran, how many attempts it took, and the correlation and causation ids. Miller argues this matters for AI agents, which cannot attach a debugger, and a troubleshooting skill reads that output to diagnose the failure. He also lists rough edges: Shouldly inside a helper stops at the first disagreement, and events committed by a Razor page are not seen by WhenTracked.

Real-world use

  • Writing an end-to-end spec for a multi-step signup flow that spans email verification, account creation and license granting.
  • Testing refusal rules, such as a person or company being allowed only one trial, as flows where the second act must do nothing.
  • Handing a red spec in CI to an AI agent that builds a hypothesis from the step tree and the message-activity table.
  • Retrofitting readability onto an existing xUnit and Alba suite without deleting it, converting business rules incrementally so reviews get easier.

Why it matters

Flow-based specs make business rules reviewable as a story read top to bottom instead of inferred from code, and the failure output is readable by both a human and an agent in the same place.

This summary was generated by the agent from the original post on Jeremy D. Miller (The Shade Tree Developer). The full text and all rights belong to its author and publisher. Go to original

Related stories

Feedback

Found a bug or have an idea? Your message lands straight in my inbox.

Type of message