Skip to main content

testing-guide

Write tests for an FSH feature — xUnit + Shouldly + NSubstitute + AutoFixture, with naming and AAA conventions. Use when adding unit/handler/validator/entity tests. Full rules in .agents/rules/testing.md + integration-testing.md.

소스 정보

저장소
fullstackhero/dotnet-starter-kit
최근 소스 활동
2026년 9월 25일 01:46
감지된 SKILL.md 언어
영어
스타
6,818
포크
2,003

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
testing-guide
description
Write tests for an FSH feature — xUnit + Shouldly + NSubstitute + AutoFixture, with naming and AAA conventions. Use when adding unit/handler/validator/entity tests. Full rules in .agents/rules/testing.md + integration-testing.md.
# Testing Guide Stack: **xUnit** + **Shouldly** (`.ShouldBe`) + **NSubstitute** (`Substitute.For<>`) + **AutoFixture** (`new Fixture()`). **Not** Moq, **not** FluentAssertions. Detailed conventions + integration-test gotchas live in `.agents/rules/testing.md` and `.agents/rules/integration-testing.md`. ## Conventions - Test class: `public sealed class {Sut}Tests`; SUT field named `_sut`. - Method name: **`MethodName_Should_ExpectedBehavior[_When_Condition]`**. - Arrange-Act-Assert with `// Arrange` / `// Act` / `// Assert`; group with `#region` (Happy Path / Guards / Edge Cases). - Mocks via `Substitute.For<IService>()`; assert calls with `.Received(1).X(arg, Arg.Any<CancellationToken>())`. - When asserting a forwarded `CancellationToken`, assert the **specific** token, not the default (NSubstitute fills optional params with `default`). ## Handler test ```csharp public sealed class Create{Entity}CommandHandlerTests { private readonly {X}DbContext _db; // or Substitute.For<IService>() for service deps private readonly Create{Entity}CommandHandler _sut; private readonly IFixture _fixture = new Fixture(); public Create{Entity}CommandHandlerTests() { _db = /* in-memory or test DbContext */; _sut = new Create{Entity}CommandHandler(_db); } [Fact] public async Task Handle_Should_PersistEntity_And_ReturnId() { // Arrange var command = new Create{Entity}Command(_fixture.Create<string>(), 9.99m, "USD"); // Act var id = await _sut.Handle(command, CancellationToken.None); // Assert id.ShouldNotBe(Guid.Empty); } } ``` Service-dependency example (NSubstitute): ```csharp _userService = Substitute.For<IUserService>(); // Act … then: await _userService.Received(1).ToggleStatusAsync(true, command.UserId, Arg.Any<CancellationToken>()); ``` ## Validator test ```csharp public sealed class Create{Entity}CommandValidatorTests { private readonly Create{Entity}CommandValidator _sut = new(); [Theory] [InlineData("")] public void Validate_Should_Fail_When_NameInvalid(string name) { var result = _sut.Validate(new Create{Entity}Command(name, 1m, "USD")); result.IsValid.ShouldBeFalse(); result.Errors.ShouldContain(e => e.PropertyName == nameof(Create{Entity}Command.Name)); } } ``` ## Entity / domain test (no mocks) ```csharp [Fact] public void Create_Should_RaiseCreatedEvent() { var entity = {Entity}.Create("Test", Money.Zero()); entity.Id.ShouldNotBe(Guid.Empty); entity.DomainEvents.ShouldContain(e => e is {Entity}CreatedDomainEvent); } ``` ## Architecture tests (guardrails — keep green) `Architecture.Tests` (NetArchTest) enforce: module boundaries (cross-module refs only via `.Contracts`), tenant-isolation rules, handlers `sealed`, and **every command/paginated-query handler has a validator**. Don't weaken these to make a change pass — fix the code. ## Integration tests `Integration.Tests` runs over real Postgres/Redis/RustFS via Testcontainers — **Docker required**. Set the Finbuckle tenant context inline, rewire `IStorageService` post-registration for RustFS (S3), force long-polling for SignalR. All detailed in `.agents/rules/integration-testing.md`. ## Run ```bash dotnet test src/Tests/{X}.Tests dotnet test src/Tests/Architecture.Tests dotnet test src/FSH.Starter.slnx --collect "XPlat Code Coverage" --settings coverage.runsettings ```
GitHub에서 보기