一键导入
csharp-unit-tests
Best practices for C# unit and integration testing with NUnit, FluentAssertions and NSubstitute. Use this when writing or reviewing C# tests.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Best practices for C# unit and integration testing with NUnit, FluentAssertions and NSubstitute. Use this when writing or reviewing C# tests.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Best practices for Angular unit testing with Jest in the blueprint frontend. Use this when writing or reviewing Angular/TypeScript spec files.
Enigmatry Entry Blueprint .NET 9 Web API patterns covering MediatR, Autofac, FluentValidation, and vertical slice architecture. Use this when adding or modifying .NET API features, handlers, validators, or controllers.
Best practices for Azure DevOps Pipeline YAML files in the Enigmatry Entry Blueprint project. Use this when creating, editing, or reviewing Azure DevOps CI/CD pipeline YAML.
Enigmatry Entry Blueprint code review checklist covering .NET 9 and Angular/TypeScript. Use this when reviewing or performing a code review on blueprint changes.
C# 13 coding standards for the YOS project — naming, formatting, nullability, and testing conventions. Use this when writing, reviewing, or refactoring any C# (.cs) file.
TypeScript 5.x / ES2022 coding standards for the YOS Angular project — naming, formatting, type system, async patterns, and architecture conventions. Use this when writing, reviewing, or refactoring any TypeScript (.ts) file.
| name | csharp-unit-tests |
| description | Best practices for C# unit and integration testing with NUnit, FluentAssertions and NSubstitute. Use this when writing or reviewing C# tests. |
[Test] / [TestCase] / [TestCaseSource])Assert.*Substitute.For<T>() in [SetUp]Microsoft.AspNetCore.Mvc.Testing (WebApplicationFactory<T>)Fixture suffix: Section.cs → SectionFixture.csFixture suffix do not need [TestFixture] — NUnit discovers them automatically via their [Test] methodsinternal sealedTest method names use descriptive camelCase — no underscores, no prefixes like Constructor_ or ToString_:
// ✅ correct
WhenEndExceedsMaxLengthThrows
ToStringReturnsEndValue
MissingSectionsLineThrows
WithValidRangeCreatesSection
// ❌ avoid
Constructor_WhenEndExceedsMaxLength_ThrowsArgumentOutOfRangeException
Parse_MissingSectionsLine_ThrowsFormatException
Separate Arrange / Act / Assert with a blank line only — never write // Arrange, // Act, or // Assert comments:
[Test]
public void WhenStartEqualsEndThrows()
{
var act = () => new Section(1000, 1000);
act.Should().Throw<ArgumentException>();
}
[Test]
public void ToStringReturnsEndValue()
{
var section = new Section(0, 2500);
var result = section.ToString();
result.Should().Be("2500");
}
Always scan for duplication before writing a new [Test] method. If two or more tests share identical body structure and differ only in one or more literal values (strings, numbers, enum values), they MUST be collapsed into a single [TestCase]-parameterized method.
// ❌ avoid — identical structure, only the expected string differs
[Test] public void ActiveStatusHasCorrectName() { ... status.Name.Should().Be("Active"); }
[Test] public void InactiveStatusHasCorrectName() { ... status.Name.Should().Be("Inactive"); }
// ✅ correct — collapsed into one parameterized test
[TestCase(UserStatusId.Active, "Active")]
[TestCase(UserStatusId.Inactive, "Inactive")]
public void StatusHasCorrectName(UserStatusId id, string expected)
{
var status = UserStatus.FromValue(id.Value);
status.Name.Should().Be(expected);
}
Use [TestCase] whenever the same assertion logic applies to multiple input values:
[TestCase("")]
[TestCase(" ")]
[TestCase(null)]
public void CreateUserWithEmptyNameThrows(string? name)
{
var act = () => new UserBuilder().WithFullName(name!).Build();
act.Should().Throw<ArgumentException>();
}
Use [TestCaseSource] when test data is more complex or reused across multiple tests:
private static readonly object[][] InvalidEmails =
[
["not-an-email"],
["missing@domain"],
["@nodomain.com"],
];
[TestCaseSource(nameof(InvalidEmails))]
public void CreateUserWithInvalidEmailThrows(string email)
{
var act = () => new UserBuilder().WithEmailAddress(email).Build();
act.Should().Throw<ArgumentException>();
}
Create mocks in [SetUp]; never share mutable mock state across tests:
private IMyService _myService = null!;
[SetUp]
public void SetUp() => _myService = Substitute.For<IMyService>();
Always use a lambda + .Should().Throw<T>() — never Assert.Throws:
var act = () => new Section(500, 100);
act.Should().Throw<ArgumentException>();
Use Verify for integration tests and any test that validates complex output (multi-line strings, JSON responses, serialized objects). Verify stores approved snapshots in .verified.txt files next to the test source.
[Test]
public async Task GetConfigurationMatchesSnapshot()
{
var response = await _client.GetAsync("/configuration");
var body = await response.Content.ReadAsStringAsync();
var settings = new VerifySettings();
settings.ScrubLinesContaining("time-dependent content");
await Verify(body, settings);
}
.received.txt file — review it and rename/copy to .verified.txt to approve..verified.txt files alongside the tests.IntegrationFixtureBase (in Api.Tests/Infrastructure/Api/) which wraps WebApplicationFactory, TestDatabase (Testcontainers SQL Server), and per-test scope management.[SetUp] method using the builder pattern (e.g. new UserBuilder().With*().Build()) and AddAndSaveChanges(entity).Client.GetAsync<T>(url) / Client.PostAsync<TRequest, TResponse>(url, body) helpers from the test HTTP client.await Verify(response) (Verify.NUnit snapshot files).[Category("integration")] so they can be filtered in CI: dotnet test --filter "Category=integration".[Category("integration")]
public class ProductsControllerFixture : IntegrationFixtureBase
{
private Product _product = null!;
[SetUp]
public void SetUp()
{
_product = new ProductBuilder()
.WithName("Test Product")
.WithCode("BKXX001");
AddAndSaveChanges(_product);
}
[Test]
public async Task TestGetById()
{
var response = await Client.GetAsync<GetProductDetails.Response>($"api/products/{_product.Id}");
await Verify(response);
}
}
[TestFixture] to classes whose names end with Fixture.// Arrange, // Act, // Assert comments.Assert.That — use FluentAssertions only.[Test] methods for cases that differ only in input values — use [TestCase] or [TestCaseSource] instead. Always check for this before writing any new [Test] method.