Source profileQuality 77/100

affaan-m/ECC/skills/csharp-testing/SKILL.md

csharp-testing

C# and .NET testing patterns with xUnit, FluentAssertions, mocking, integration tests, and test organization best practices.

Source repository stars
234,327
Declared platforms
0
Static risk flags
0
Last source update
2026-07-27
Source checked
2026-07-28

Decision brief

What it does—and where it fits

Comprehensive testing patterns for .NET applications using xUnit, FluentAssertions, and modern testing practices.

Best for

    Not for

    • Tasks that require unconfirmed production actions or broad system permissions.
    • Environments where the pinned source and install steps cannot be inspected.

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/affaan-m/ECC --skill "skills/csharp-testing"
    Safe inspection promptEditorial

    Inspect the Agent Skill "csharp-testing" from https://github.com/affaan-m/ECC/blob/4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38/skills/csharp-testing/SKILL.md at commit 4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      WebApplicationFactory Setup

      Review the “WebApplicationFactory Setup” section in the pinned source before continuing.

      Review and apply the “WebApplicationFactory Setup” source section.
    2. 02

      When to Activate

      Writing new tests for C code

      Writing new tests for C codeReviewing test quality and coverageSetting up test infrastructure for .NET projects
    3. 03

      Test Framework Stack

      Review the “Test Framework Stack” section in the pinned source before continuing.

      Review and apply the “Test Framework Stack” source section.
    4. 04

      Unit Test Structure

      Review the “Unit Test Structure” section in the pinned source before continuing.

      Review and apply the “Unit Test Structure” source section.
    5. 05

      Arrange-Act-Assert

      Review the “Arrange-Act-Assert” section in the pinned source before continuing.

      Review and apply the “Arrange-Act-Assert” source section.

    Permission review

    Static risk signals and limitations

    No configured static risk pattern was detected

    This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score77/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars234,327SourceRepository attention, not individual Skill quality
    Compatibility0 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    affaan-m/ECC
    Skill path
    skills/csharp-testing/SKILL.md
    Commit
    4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38
    License
    MIT
    Collected
    2026-07-28
    Default branch
    main
    View the original SKILL.md

    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

    ToolPurpose
    xUnitTest framework (preferred for .NET)
    FluentAssertionsReadable assertion syntax
    NSubstitute or MoqMocking dependencies
    TestcontainersReal infrastructure in integration tests
    WebApplicationFactoryASP.NET Core integration tests
    BogusRealistic test data generation

    Unit Test Structure

    Arrange-Act-Assert

    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

    [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

    [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.CustomerId == request.CustomerId),
            Arg.Any<CancellationToken>());
    }
    

    ASP.NET Core Integration Tests

    WebApplicationFactory Setup

    public sealed class OrderApiTests : IClassFixture<WebApplicationFactory<Program>>
    {
        private readonly HttpClient _client;
    
        public OrderApiTests(WebApplicationFactory<Program> factory)
        {
            _client = factory.WithWebHostBuilder(builder =>
            {
                builder.ConfigureServices(services =>
                {
                    // Replace real DB with in-memory for tests
                    services.RemoveAll<DbContextOptions<AppDbContext>>();
                    services.AddDbContext<AppDbContext>(options =>
                        options.UseInMemoryDatabase("TestDb"));
                });
            }).CreateClient();
        }
    
        [Fact]
        public async Task GetOrder_Returns404_WhenNotFound()
        {
            var response = await _client.GetAsync($"/api/orders/{Guid.NewGuid()}");
    
            response.StatusCode.Should().Be(HttpStatusCode.NotFound);
        }
    
        [Fact]
        public async Task CreateOrder_Returns201_WithValidRequest()
        {
            var request = new CreateOrderRequest
            {
                CustomerId = "cust-1",
                Items = [new("SKU-001", 1, 19.99m)]
            };
    
            var response = await _client.PostAsJsonAsync("/api/orders", request);
    
            response.StatusCode.Should().Be(HttpStatusCode.Created);
            response.Headers.Location.Should().NotBeNull();
        }
    }
    

    Testing with Testcontainers

    public sealed class PostgresOrderRepositoryTests : IAsyncLifetime
    {
        private readonly PostgreSqlContainer _postgres = new PostgreSqlBuilder()
            .WithImage("postgres:16-alpine")
            .Build();
    
        private AppDbContext _db = null!;
    
        public async Task InitializeAsync()
        {
            await _postgres.StartAsync();
            var options = new DbContextOptionsBuilder<AppDbContext>()
                .UseNpgsql(_postgres.GetConnectionString())
                .Options;
            _db = new AppDbContext(options);
            await _db.Database.MigrateAsync();
        }
    
        public async Task DisposeAsync()
        {
            await _db.DisposeAsync();
            await _postgres.DisposeAsync();
        }
    
        [Fact]
        public async Task AddAsync_PersistsOrder()
        {
            var repo = new SqlOrderRepository(_db);
            var order = Order.Create("cust-1", [new OrderItem("SKU-001", 2, 10m)]);
    
            await repo.AddAsync(order, CancellationToken.None);
    
            var found = await repo.FindByIdAsync(order.Id, CancellationToken.None);
            found.Should().NotBeNull();
            found!.Items.Should().HaveCount(1);
        }
    }
    

    Test Organization

    tests/
      MyApp.UnitTests/
        Services/
          OrderServiceTests.cs
          PaymentServiceTests.cs
        Validators/
          EmailValidatorTests.cs
      MyApp.IntegrationTests/
        Api/
          OrderApiTests.cs
        Repositories/
          OrderRepositoryTests.cs
      MyApp.TestHelpers/
        Builders/
          OrderBuilder.cs
        Fixtures/
          DatabaseFixture.cs
    

    Test Data Builders

    public sealed class OrderBuilder
    {
        private string _customerId = "cust-default";
        private readonly List<OrderItem> _items = [new("SKU-001", 1, 10m)];
    
        public OrderBuilder WithCustomer(string customerId)
        {
            _customerId = customerId;
            return this;
        }
    
        public OrderBuilder WithItem(string sku, int quantity, decimal price)
        {
            _items.Add(new OrderItem(sku, quantity, price));
            return this;
        }
    
        public Order Build() => Order.Create(_customerId, _items);
    }
    
    // Usage in tests
    var order = new OrderBuilder()
        .WithCustomer("cust-vip")
        .WithItem("SKU-PREMIUM", 3, 99.99m)
        .Build();
    

    Common Anti-Patterns

    Anti-PatternFix
    Testing implementation detailsTest behavior and outcomes
    Shared mutable test stateFresh instance per test (xUnit does this via constructors)
    Thread.Sleep in async testsUse Task.Delay with timeout, or polling helpers
    Asserting on ToString() outputAssert on typed properties
    One giant assertion per testOne logical assertion per test
    Test names describing implementationName by behavior: Method_ExpectedResult_WhenCondition
    Ignoring CancellationTokenAlways pass and verify cancellation

    Running Tests

    # Run all tests
    dotnet test
    
    # Run with coverage
    dotnet test --collect:"XPlat Code Coverage"
    
    # Run specific project
    dotnet test tests/MyApp.UnitTests/
    
    # Filter by test name
    dotnet test --filter "FullyQualifiedName~OrderService"
    
    # Watch mode during development
    dotnet watch test --project tests/MyApp.UnitTests/
    

    Alternatives

    Compare before choosing