Append Assertions
Every Append and AppendMany call returns an IAppendResult. The Cratis.Chronicle.Testing package provides built-in Should* extension methods for asserting on these results.
Assertion reference
Section titled “Assertion reference”| Method | Asserts |
|---|---|
result.ShouldBeSuccessful() | Operation succeeded with no violations or errors |
result.ShouldBeFailed() | Operation failed (any violation or error) |
result.ShouldHaveConstraintViolations() | At least one constraint violation is present |
result.ShouldNotHaveConstraintViolations() | No constraint violations are present |
result.ShouldHaveConstraintViolationFor(name) | A violation for the named constraint is present |
result.ShouldHaveConstraintViolation(name) | Singular alias for ShouldHaveConstraintViolationFor(name) |
result.ShouldHaveConcurrencyViolations() | At least one concurrency violation is present |
result.ShouldNotHaveConcurrencyViolations() | No concurrency violations are present |
result.ShouldHaveErrors() | At least one error is present |
result.ShouldNotHaveErrors() | No errors are present |
All Should* methods throw AppendResultAssertionException on failure. The exception message describes all violations and errors found, making it easy to diagnose failures.
Happy path assertion
Section titled “Happy path assertion”using Cratis.Chronicle.Events;using Cratis.Chronicle.Events.Constraints;using Cratis.Chronicle.Testing.EventSequences;
[EventType]public record TestingAssertionsAuthorRegistered([property: Unique(name: "TestingAssertionsUniqueAuthorName")] string Name);
public static class TestingAssertionsHappyPath{ public static async Task Run() { var scenario = new EventScenario(); var authorId = EventSourceId.New(); var result = await scenario.EventLog.Append(authorId, new TestingAssertionsAuthorRegistered("Jane Smith")); result.ShouldBeSuccessful(); }}Failure assertions
Section titled “Failure assertions”using Cratis.Chronicle.Events;using Cratis.Chronicle.Testing.EventSequences;
public static class TestingAssertionsFailure{ public static async Task Run() { var scenario = new EventScenario(); var authorId = EventSourceId.New(); var newAuthorId = EventSourceId.New();
await scenario.Given .ForEventSource(authorId) .Events(new TestingAssertionsAuthorRegistered("Jane Smith"));
var result = await scenario.EventLog.Append(newAuthorId, new TestingAssertionsAuthorRegistered("Jane Smith")); result.ShouldBeFailed(); }}Constraint violation assertions
Section titled “Constraint violation assertions”Assert that a specific named constraint fired:
using Cratis.Chronicle.EventSequences;
public static class TestingAssertionsConstraintViolation{ public static void Assert(AppendResult result) { result.ShouldHaveConstraintViolations(); result.ShouldHaveConstraintViolationFor("TestingAssertionsUniqueAuthorName"); }}Assert that no constraint violations occurred:
using Cratis.Chronicle.EventSequences;
public static class TestingAssertionsNoConstraintViolation{ public static void Assert(AppendResult result) => result.ShouldNotHaveConstraintViolations();}Concurrency violation assertions
Section titled “Concurrency violation assertions”using Cratis.Chronicle.EventSequences;
public static class TestingAssertionsConcurrency{ public static void AssertHasViolations(AppendResult result) => result.ShouldHaveConcurrencyViolations();
public static void AssertNoViolations(AppendResult result) => result.ShouldNotHaveConcurrencyViolations();}Error assertions
Section titled “Error assertions”using Cratis.Chronicle.EventSequences;
public static class TestingAssertionsErrors{ public static void AssertHasErrors(AppendResult result) => result.ShouldHaveErrors();
public static void AssertNoErrors(AppendResult result) => result.ShouldNotHaveErrors();}For asserting on the event sequence itself (tail sequence number, event types, and event content), see Event Sequence Assertions.
Example: testing a constraint violation end-to-end
Section titled “Example: testing a constraint violation end-to-end”Pre-seed state with Given, act with When, then assert on the captured scenario.AppendResult — including constraints declared on ConceptAs<T> properties:
using Cratis.Concepts;using Cratis.Chronicle.Events;using Cratis.Chronicle.Events.Constraints;using Cratis.Chronicle.EventSequences;using Cratis.Chronicle.Testing.EventSequences;using Cratis.Specifications;using Xunit;
// Email is a ConceptAs<string> — uniqueness on a domain value works in-process,// so the constraint no longer needs to be hidden behind #if !DEBUG.public record TestingAssertionsEmail(string Value) : ConceptAs<string>(Value);
[EventType]public record TestingAssertionsAuthorRegisteredWithEmail([property: Unique("TestingAssertionsUniqueAuthorEmail")] TestingAssertionsEmail Email);
public class when_registering_an_author_with_a_taken_email : Specification, IDisposable{ readonly EventScenario _scenario = new(); AppendResult _result = default!;
Task Establish() => _scenario.Given .ForEventSource(EventSourceId.New()) .Events(new TestingAssertionsAuthorRegisteredWithEmail(new("john@doe.com")));
async Task Because() => _result = await _scenario.When .ForEventSource(EventSourceId.New()) .Events(new TestingAssertionsAuthorRegisteredWithEmail(new("john@doe.com")));
[Fact] void should_be_rejected() => _result.ShouldBeFailed();
[Fact] void should_report_the_constraint() => _result.ShouldHaveConstraintViolation("TestingAssertionsUniqueAuthorEmail");
public void Dispose() => _scenario.Dispose();}