Skip to main content

FluentAssertions Integration

For developers who prefer the widely-used FluentAssertions library, NPipeline.Extensions.Testing.FluentAssertions provides a seamless integration. This extension allows you to use the rich, fluent, and highly readable assertion syntax of FluentAssertions to validate your pipeline's output and execution results.

Installation

To use this integration, add both the FluentAssertions and the NPipeline extension package to your test project:

dotnet add package FluentAssertions
dotnet add package NPipeline.Extensions.Testing.FluentAssertions

Pipeline Execution Result Assertions

With NPipeline.Extensions.Testing, you can use PipelineTestHarness<T> to test your pipelines with fluent assertions on the execution results:

using NPipeline.Extensions.Testing;
using NPipeline.Extensions.Testing.FluentAssertions;
using Xunit;
using FluentAssertions;

public class MyPipelineTests
{
[Fact]
public async Task Pipeline_ShouldCompleteSuccessfully()
{
// Arrange & Act
var result = await new PipelineTestHarness<MyPipeline>()
.WithParameter("input", testData)
.RunAsync();

// Assert - fluent API with FluentAssertions
result
.ShouldBeSuccessful()
.ShouldHaveNoErrors()
.ShouldCompleteWithin(TimeSpan.FromSeconds(5));
}

[Fact]
public async Task Pipeline_ShouldCaptureErrorsGracefully()
{
// Arrange & Act
var result = await new PipelineTestHarness<MyPipeline>()
.WithParameter("input", invalidData)
.CaptureErrors()
.RunAsync();

// Assert
result
.ShouldFail()
.ShouldHaveErrorOfType<InvalidOperationException>()
.ShouldHaveErrorCount(1);
}
}

Pipeline Result Assertions

The FluentAssertions integration provides these assertion extension methods:

MethodDescription
ShouldBeSuccessful()Assert pipeline executed successfully (no uncaught exceptions)
ShouldFail()Assert pipeline execution failed
ShouldHaveNoErrors()Assert no errors were captured
ShouldHaveErrorCount(int)Assert specific number of errors were captured
ShouldHaveErrorOfType<TException>()Assert at least one error of a specific type
ShouldCompleteWithin(TimeSpan)Assert execution completed within duration

All methods return the result for fluent chaining.

Sink Node Assertions

For asserting on individual sink nodes, the existing sink extensions still apply:

using NPipeline.Extensions.Testing.FluentAssertions;
using FluentAssertions;

[Fact]
public async Task Sink_Should_Receive_Data()
{
// Arrange
var sink = new InMemorySinkNode<string>();
var context = new PipelineContext();
var data = new ListDataPipe<string>(new[] { "a", "b", "c" });

// Act
await sink.ExecuteAsync(data, context, CancellationToken.None);

// Assert
sink.ShouldHaveReceived(3);
sink.ShouldContain("a");
sink.ShouldContain(x => x.Length == 1);
sink.ShouldOnlyContain(x => !string.IsNullOrEmpty(x));
}

Complete Example: Testing with Error Handling

using NPipeline.Extensions.Testing;
using NPipeline.Extensions.Testing.FluentAssertions;
using Xunit;
using FluentAssertions;

public class PipelineWithErrorHandlingTests
{
[Fact]
public async Task Pipeline_With_Error_Capturing_Should_Report_Errors()
{
// Arrange
var result = await new PipelineTestHarness<PipelineThatCanFail>()
.WithParameter("input", problemData)
.CaptureErrors(PipelineErrorDecision.ContinueWithoutNode)
.RunAsync();

// Assert - fluent assertions make this very readable
result
.ShouldHaveErrorCount(2)
.ShouldHaveErrorOfType<ValidationException>()
.ShouldCompleteWithin(TimeSpan.FromSeconds(10));

// Can also access the sink
var sink = result.GetSink<InMemorySinkNode<ProcessedData>>();
sink.ShouldHaveReceived(expectedSuccessfulCount);
}

[Fact]
public async Task Pipeline_Should_Preserve_Custom_Error_Handlers()
{
// Arrange - create a context with custom error handler
var errorLog = new List<string>();
var customHandler = new LoggingErrorHandler(errorLog);

var context = new PipelineContext();
context.PipelineErrorHandler = customHandler;

// Act - test harness will chain custom handler with capturing handler
var result = await new PipelineTestHarness<MyPipeline>(context)
.WithParameter("input", testData)
.CaptureErrors()
.RunAsync();

// Assert - both error capturing AND custom logging happened
errorLog.Should().NotBeEmpty("custom handler should have logged");
result.Errors.Should().NotBeEmpty("errors should be captured");
}
}

Why Use FluentAssertions?

  • Strongly-Typed and Discoverable: The fluent API guides you to the right assertions, with excellent IntelliSense support.
  • Rich Collection Support: FluentAssertions has an extensive set of assertions for collections, allowing you to check for order, equivalency, subsets, and more.
  • Clear Failure Messages: When a test fails, FluentAssertions provides exceptionally clear and detailed error messages that pinpoint the exact cause of the failure, making debugging much faster.
  • Chainable Assertions: Pipeline result assertions chain together, allowing elegant multi-assertion test patterns.

This integration allows you to leverage the full power of FluentAssertions to write robust and maintainable tests for your NPipelines.

Next Steps

  • AwesomeAssertions: If you prefer AwesomeAssertions, NPipeline also provides a similar integration for that library.
  • Testing Extensions: Return to the main testing documentation for more testing patterns and utilities.
  • Connectors: Explore the available connectors for reading from and writing to external systems like CSV files.