Test a train's decisions
Source ScriptedDecider.cs
ScriptedDecider, in Trax.Core.Decisions, answers each question with what the test says and
keeps every request it was asked, so a test can send a train down any track without a model.
The train here is an underwriter whose Approve and Decline tracks need a confidence of 0.95
and which declares no Otherwise track (see Decisions).
Script the answers
The examples use NUnit and AwesomeAssertions, as Trax's own tests do. Hand the train a
ScriptedDecider that answers the way the case needs, run it, and assert on the
result and on Requests:
using AwesomeAssertions;
using NUnit.Framework;
using Trax.Core.Decisions;
public class UnderwriteLoanTests
{
[Test]
public async Task AnUnsureApproval_FailsTheRun()
{
var decider = new ScriptedDecider().Choose(Underwriting.Approve, confidence: 0.62);
var result = await new UnderwriteLoan(decider).RunEither(
new LoanApplication("a3", 700, 0.4m)
);
result.IsLeft.Should().BeTrue(); // below Approve's 0.95, and no Otherwise
decider.Requests.Should().ContainSingle(); // what a model would have been shown
}
[Test]
public async Task ManualReview_NeedsNoConfidence()
{
var decider = new ScriptedDecider().Choose(Underwriting.ManualReview, confidence: 0.1);
var decision = await new UnderwriteLoan(decider).Run(new LoanApplication("a4", 690, 0.4m));
decision.Outcome.Should().Be("assigned to an underwriter");
}
}The train takes its decider as a constructor argument and hands it to the chain with
AddServices, so the test needs no container. A train that resolves its decider from the
container gets the ScriptedDecider registered as its IDecider instead.
What ScriptedDecider can answer
| Method | Answers |
|---|---|
Choose<TTrack>(choice, confidence, probabilities, model) | The choice question about TTrack with that member. |
Score<TLevel>(score, confidence, model) | The score question about TLevel with that position. |
YesNo<TQuestion>(probability, model) | The yes or no question about TQuestion. |
Answer(key, answer) | The question with that key, with whatever the function returns, including an answer that does not fit, to test how the train refuses it. |
Throws(exception) | Nothing: every request fails with the exception, as a model that is down would. |
| Property | Holds |
|---|---|
Requests | Every DecisionRequest it was asked, in order: the train's name, the state, and the questions with their words and options. |
LastToken | The cancellation token the last request was given. |
A question it has no answer for is left unanswered, which fails the run the way a model that skipped one would.
Check the declaration as well
A scripted answer sends the run down one track. To check every track, verify the declared chain, which is what the host's startup check does for each registered train:
using AwesomeAssertions;
using NUnit.Framework;
using Trax.Core.Decisions;
using Trax.Core.Monad;
public class UnderwriteLoanChainTests
{
[Test]
public void EveryTrackVerifies() =>
ChainVerification
.Verify(
new UnderwriteLoan(new ScriptedDecider()).DeclaredChain(),
typeof(LoanApplication),
typeof(LoanDecision)
)
.Should()
.BeEmpty();
}See DeclaredChain for what the recording holds.