agentskills.codes
TD

tdd-workflow

Use this skill when writing new features, fixing bugs, or refactoring code in Verendar. Enforces test-driven development with 80%+ coverage using xUnit, FluentAssertions, NSubstitute, and Testcontainers. Activate proactively whenever the user is about to write new application code, fix a bug, or add

Install

mkdir -p .claude/skills/tdd-workflow-dreamlab2025 && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/14539" && unzip -o skill.zip -d .claude/skills/tdd-workflow-dreamlab2025 && rm skill.zip

Installs to .claude/skills/tdd-workflow-dreamlab2025

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Use this skill when writing new features, fixing bugs, or refactoring code in Verendar. Enforces test-driven development with 80%+ coverage using xUnit, FluentAssertions, NSubstitute, and Testcontainers. Activate proactively whenever the user is about to write new application code, fix a bug, or add an endpoint — tests must come first.
337 chars✓ has a “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)

About this skill

TDD Workflow — .NET / Verendar

The Cycle

RED → GREEN → REFACTOR — always in that order. Never write implementation before a failing test exists.


Step 1: Define the Behaviour (User Story)

As a [role], I want to [action], so that [benefit].

Example:
As a user, I want to cancel a booking,
so that I can get a refund if my plans change.

Step 2: Write Failing Tests (RED)

// Verendar.Garage.Tests/Services/BookingServiceTests.cs
[Fact]
public async Task CancelAsync_PendingBooking_PublishesCancelledEvent()
{
    // Arrange
    var booking = BookingFaker.Pending();
    _bookingRepo.FindByIdAsync(booking.Id).Returns(booking);

    // Act
    var result = await _sut.CancelAsync(booking.Id, booking.UserId, CancellationToken.None);

    // Assert
    result.IsSuccess.Should().BeTrue();
    await _publishEndpoint.Received(1)
        .Publish(Arg.Any<BookingCancelledEvent>(), Arg.Any<CancellationToken>());
}

[Fact]
public async Task CancelAsync_AlreadyCancelled_ReturnsFailure()
{
    var booking = BookingFaker.Cancelled();
    _bookingRepo.FindByIdAsync(booking.Id).Returns(booking);

    var result = await _sut.CancelAsync(booking.Id, booking.UserId, CancellationToken.None);

    result.IsSuccess.Should().BeFalse();
}

Run: task test PROJECT=Garage/Verendar.Garage.Tests → tests must fail.


Step 3: Implement Minimally (GREEN)

Write the least code needed to make the tests pass. No extras.


Step 4: Refactor

Clean up while keeping all tests green. Only after GREEN.


Step 5: Verify Coverage

task test:all
dotnet test --collect:"XPlat Code Coverage"

Minimum 80% overall. 100% for payment and auth logic.


Test Types

TypeToolPurpose
UnitxUnit + FluentAssertions + NSubstituteService logic, domain rules, pure functions
IntegrationWebApplicationFactory + TestcontainersAPI endpoints against real PostgreSQL
ConsumerMassTransit TestHarnessEvent consumers in isolation

Unit Test Setup

public class BookingServiceTests
{
    private readonly IUnitOfWork _uow = Substitute.For<IUnitOfWork>();
    private readonly IPublishEndpoint _pub = Substitute.For<IPublishEndpoint>();
    private readonly BookingService _sut;

    public BookingServiceTests()
    {
        _uow.Bookings.Returns(Substitute.For<IBookingRepository>());
        _sut = new BookingService(_uow, _pub, NullLogger<BookingService>.Instance);
    }
}

Integration Test Setup

public class BookingApiTests(GarageWebFactory factory) : IClassFixture<GarageWebFactory>
{
    private readonly HttpClient _client = factory.CreateClient();

    [Fact]
    public async Task POST_Cancel_Returns200()
    {
        _client.DefaultRequestHeaders.Authorization =
            new AuthenticationHeaderValue("Bearer", factory.GenerateUserToken(Guid.NewGuid()));

        var response = await _client.PostAsync($"/api/bookings/{bookingId}/cancel", null);

        response.StatusCode.Should().Be(HttpStatusCode.OK);
    }
}

See references/dotnet-patterns.md for the full GarageWebFactory with Testcontainers setup.


Anti-Patterns

WrongRight
Write implementation firstTests first — always RED before GREEN
In-memory DB in integration testsTestcontainers (real PostgreSQL)
MoqNSubstitute
Test internal stateTest observable behaviour (return values, published events)
Tests that depend on each otherEach test sets up its own data

Run Commands

task test:all                                         # all tests
task test PROJECT=Garage/Verendar.Garage.Tests        # single project
dotnet test --filter "FullyQualifiedName~CancelAsync" # single test

Search skills

Search the agent skills registry