
Csharp Testing
Structure C# unit and integration tests with xUnit, FluentAssertions, mocks, Testcontainers, and WebApplicationFactory.
Overview
C# Testing Patterns is an agent skill most often used in Ship (also Build backend) that applies xUnit, FluentAssertions, and .NET integration-test tooling to write and harden C# test suites.
Install
npx skills add https://github.com/affaan-m/everything-claude-code --skill csharp-testingWhat is this skill?
- Seven-tool test stack table: xUnit, FluentAssertions, NSubstitute/Moq, Testcontainers, WebApplicationFactory, Bogus
- Arrange-Act-Assert unit test layout with `Substitute.For` dependencies and async `Fact` examples
- Guidance for integration tests with real infrastructure and ASP.NET Core hosting factories
- Activation hooks for new tests, coverage review, test infrastructure setup, and flaky-test diagnosis
- 7-tool recommended test framework stack in the documentation table
Adoption & trust: 3.1k installs on skills.sh; 210k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
Your .NET codebase lacks consistent test layout, weak mocks, or slow flaky integration runs and you need a repeatable pattern before you ship.
Who is it for?
Solo builders on ASP.NET Core or .NET libraries who want agent help matching ECC-style testing discipline.
Skip if: Non-.NET stacks, or teams that only want manual QA checklists without automated test code.
When should I use this skill?
Writing new tests for C# code, reviewing test quality and coverage, setting up test infrastructure for .NET projects, or debugging flaky or slow tests.
What do I get? / Deliverables
You get AAA-structured xUnit tests, clear FluentAssertions, and a documented stack for mocks and ASP.NET integration tests that CI can trust.
- xUnit test classes with AAA structure
- Mocked unit tests and optional integration test setup
- Test infrastructure patterns for ASP.NET Core
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
The skill activates for test authoring, coverage review, and flaky-test debugging—Ship → testing is the canonical shelf even when you write tests alongside feature work in Build. It encodes Arrange-Act-Assert, mocking, and ASP.NET Core integration patterns—the core QA lane before release, not greenfield UI design.
Where it fits
Add `OrderServiceTests` with substituted `IOrderRepository` and assert async place-order behavior before release.
Scaffold xUnit project and Bogus fixtures while implementing a new minimal API endpoint.
Review whether tests use real assertions versus trivial `true` checks during a pre-merge pass.
Split slow Testcontainers suites from fast unit tests to shorten feedback loops.
How it compares
Opinionated .NET test playbook—broader than a single linter rule, narrower than a full CI/CD deploy skill.
Common Questions / FAQ
Who is csharp-testing for?
Indie and solo developers shipping C# or ASP.NET Core products with Claude Code, Cursor, or Codex who want structured unit and integration tests.
When should I use csharp-testing?
In Ship when writing or reviewing tests and debugging flakes; in Build backend when scaffolding test projects alongside new APIs—especially on the skill’s four activation triggers.
Is csharp-testing safe to install?
It is procedural testing guidance without inherent runtime risk—still review the Security Audits panel on this Prism page for the parent skill package.
SKILL.md
READMESKILL.md - Csharp Testing
# C# Testing Patterns Comprehensive testing patterns for .NET applications using xUnit, FluentAssertions, and modern testing practices. ## When to Activate - Writing new tests for C# code - Reviewing test quality and coverage - Setting up test infrastructure for .NET projects - Debugging flaky or slow tests ## Test Framework Stack | Tool | Purpose | |---|---| | **xUnit** | Test framework (preferred for .NET) | | **FluentAssertions** | Readable assertion syntax | | **NSubstitute** or **Moq** | Mocking dependencies | | **Testcontainers** | Real infrastructure in integration tests | | **WebApplicationFactory** | ASP.NET Core integration tests | | **Bogus** | Realistic test data generation | ## Unit Test Structure ### Arrange-Act-Assert ```csharp public sealed class OrderServiceTests { private readonly IOrderRepository _repository = Substitute.For<IOrderRepository>(); private readonly ILogger<OrderService> _logger = Substitute.For<ILogger<OrderService>>(); private readonly OrderService _sut; public OrderServiceTests() { _sut = new OrderService(_repository, _logger); } [Fact] public async Task PlaceOrderAsync_ReturnsSuccess_WhenRequestIsValid() { // Arrange var request = new CreateOrderRequest { CustomerId = "cust-123", Items = [new OrderItem("SKU-001", 2, 29.99m)] }; // Act var result = await _sut.PlaceOrderAsync(request, CancellationToken.None); // Assert result.IsSuccess.Should().BeTrue(); result.Value.Should().NotBeNull(); result.Value!.CustomerId.Should().Be("cust-123"); } [Fact] public async Task PlaceOrderAsync_ReturnsFailure_WhenNoItems() { // Arrange var request = new CreateOrderRequest { CustomerId = "cust-123", Items = [] }; // Act var result = await _sut.PlaceOrderAsync(request, CancellationToken.None); // Assert result.IsSuccess.Should().BeFalse(); result.Error.Should().Contain("at least one item"); } } ``` ### Parameterized Tests with Theory ```csharp [Theory] [InlineData("", false)] [InlineData("a", false)] [InlineData("ab@c.d", false)] [InlineData("user@example.com", true)] [InlineData("user+tag@example.co.uk", true)] public void IsValidEmail_ReturnsExpected(string email, bool expected) { EmailValidator.IsValid(email).Should().Be(expected); } [Theory] [MemberData(nameof(InvalidOrderCases))] public async Task PlaceOrderAsync_RejectsInvalidOrders(CreateOrderRequest request, string expectedError) { var result = await _sut.PlaceOrderAsync(request, CancellationToken.None); result.IsSuccess.Should().BeFalse(); result.Error.Should().Contain(expectedError); } public static TheoryData<CreateOrderRequest, string> InvalidOrderCases => new() { { new() { CustomerId = "", Items = [ValidItem()] }, "CustomerId" }, { new() { CustomerId = "c1", Items = [] }, "at least one item" }, { new() { CustomerId = "c1", Items = [new("", 1, 10m)] }, "SKU" }, }; ``` ## Mocking with NSubstitute ```csharp [Fact] public async Task GetOrderAsync_ReturnsNull_WhenNotFound() { // Arrange var orderId = Guid.NewGuid(); _repository.FindByIdAsync(orderId, Arg.Any<CancellationToken>()) .Returns((Order?)null); // Act var result = await _sut.GetOrderAsync(orderId, CancellationToken.None); // Assert result.Should().BeNull(); } [Fact] public async Task PlaceOrderAsync_PersistsOrder() { // Arrange var request = ValidOrderRequest(); // Act await _sut.PlaceOrderAsync(request, CancellationToken.None); // Assert — verify the repository was called await _repository.Received(1).AddAsync( Arg.Is<Order>(o => o.Cust