Skip to content

.NET SDK reference

TinyConductor.Testing runs a TinyConductor engine in Embedded mode inside your xUnit test process. It needs no server, database or container, and it keeps nothing on disk. It behaves exactly like the Embedded mode server, because it is the Embedded server, running in your process: the same engine and the same API production runs. Its virtual clock stands still until your test moves it.

The SDK is not on NuGet yet. The release carries it as a package file, TinyConductor.Testing.0.1.0.nupkg: add the folder that holds it as a local package source, then dotnet add package TinyConductor.Testing. The SDK also needs the engine’s native library next to your test assembly. It is in the lib folder of the TinyConductor release archive for your platform. The planned NuGet package will carry the library for every platform (the .NET testing guide shows both steps).

Then deploy the BPMN and form resources, register worker behavior, and make record-backed assertions:

using TinyConductor.Testing;
using Xunit;
public sealed class OrderProcessTests : IDisposable
{
private readonly EmbeddedEngine _engine = new();
[Fact]
public void Order_completes_after_payment_review_period_and_confirmation()
{
_engine.DeployResource("processes/order.form");
_engine.DeployResource("processes/order-decisions.dmn");
_engine.DeployResource("processes/order.bpmn");
_engine.MockJobWorker("send-invoice").ThenComplete();
var order = _engine.CreateInstance(
"order-process",
new { orderId = 4711, amount = 99.90 });
_engine.CompleteJob("charge-card", new { charged = true });
_engine.Clock.AdvanceBy(TimeSpan.FromDays(1));
Assert.Single(_engine.UserTasks)
.Complete(new { shipped = true, carrier = "dhl" });
TinyConductorAssert.That(order)
.IsCompleted()
.HasCompletedElements(
"charge-card",
"shipping-tier",
"review-period",
"send-invoice",
"confirm-shipment")
.HasVariable("charged", true)
.HasVariable("shippingTier", "standard")
.HasVariable("shipped", true);
}
public void Dispose() => _engine.Dispose();
}

processes/order.bpmn, processes/order-decisions.dmn and processes/order.form are copies of the shared sample files in the release’s examples download: the same files the quickstart deploys into a Bundled container. The shipping-tier business rule task evaluates the deployed decision table in-process, so it needs no mock worker.

The API is synchronous. The engine runs in your process and processes one call at a time, so async would add noise to your tests without making anything run in parallel.

Moving the clock waits for the engine to settle. AdvanceBy and AdvanceTo return only when everything the move caused is visible in the records: the timers due at the new instant have fired, the engine work they caused has run, and your registered job-worker mocks have answered.

Jobs have leases, as on a server. ActivateJobs leases each job for 30 seconds of engine time; if your test moves the clock past that before it completes or fails the job, the job is offered again, and the old ActivatedJob can no longer complete it.

You can pass variables as anonymous objects, POCOs or dictionaries. Read the variables of an activated job with job.GetVariable<T>("name").

The package mirrors the assertions of compatible process-testing libraries (see Compatibility):

Process-test conceptTinyConductor.Testing
assertion entry point (assertThat(instance))TinyConductorAssert.That(instance)
isCompleted, isActive, isTerminatedIsCompleted(), IsActive(), IsTerminated()
hasCompletedElementsHasCompletedElements(params string[])
hasActiveElementsHasActiveElements(params string[])
hasVariable, hasVariablesHasVariable(name, expected), HasVariableNames(params string[])
hasNoIncidents, active incident assertionHasNoIncidents(), HasActiveIncidents()
test clock time increaseengine.Clock.AdvanceBy(TimeSpan)
test clock absolute timeengine.Clock.AdvanceTo(DateTimeOffset)
mocked job workerengine.MockJobWorker(type).ThenComplete() / ThenThrowError() / ThenHandle(...)
job activation and completionActivateJobs(type), CompleteJob(type)
user-task query and completionengine.UserTasks, UserTask.Assign(...), UserTask.Complete(...)

Some Embedded-mode JSON operations have no typed method yet. Send one of those with engine.Transport.Send(json), which returns the raw JSON response as a string. Transport details such as JsonDocument are not part of the public API. If you need an operation often, prefer adding a typed method to the package.