csharp-testing
使用 xUnit、FluentAssertions、模拟、集成测试和测试组织最佳实践的 C# 和 .NET 测试模式。
DeepseekModel
官方收录技能
质量 优秀 · 90
v1.0.0
获取
https://deepseekmodel.com/api/download.php?id=affaan-m-ecc-docs-zh-cn-skills-csharp-testing-skill-md&format=skill
下载 .skill
标准格式,含 system_prompt 与 model_config,导入任意 Agent 框架即可使用
.skill 文件中 system_prompt 字段的实际内容。
name csharp-testing description 使用 xUnit、FluentAssertions、模拟、集成测试和测试组织最佳实践的 C# 和 .NET 测试模式。 origin ECC C# 测试模式 使用 xUnit、FluentAssertions 和现代测试实践为 .NET 应用程序提供的全面测试模式。 何时使用 为 C# 代码编写新测试 审查测试质量和覆盖率 为 .NET 项目搭建测试基础设施 调试不稳定或缓慢的测试 测试框架栈 工具 用途 xUnit 测试框架(.NET 首选) FluentAssertions 可读的断言语法 NSubstitute 或 Moq 模拟依赖项 Testcontainers 集成测试中的真实基础设施 WebApplicationFactory ASP.NET Core 集成测试 Bogus 生成逼真的测试数据 单元测试结构 安排-操作-断言 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.99 m)] }; // 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" ); } } 使用 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 , 10 m)] }, "SKU" }, }; 使用 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 集成测试 WebApplicationFactory 设置 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.99 m)] }; var response = await _client.PostAsJsonAsync( "/api/orders" , request); response.StatusCode.Should().Be(HttpStatusCode.Created); response.Headers.Location.Should().NotBeNull(); } } 使用 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 , 10 m)]); await repo.AddAsync(order, CancellationToken.None); var found = await repo.FindByIdAsync(order.Id, CancellationToken.None); found.Should().NotBeNull(); found!.Items.Should().HaveCount( 1 ); } } 测试组织 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 测试数据构建器 public sealed class OrderBuilder { private string _customerId = "cust-default" ; private readonly List<OrderItem> _items = [ new ( "SKU-001" , 1 , 10 m)]; 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.99 m) .Build(); 常见反模式 反模式 修复方法 测试实现细节 测试行为和结果 共享的可变测试状态 每个测试使用新实例(xUnit 通过构造函数实现) 在异步测试中使用 Thread.Sleep 使用带超时的 Task.Delay 或轮询辅助方法 对 ToString() 输出进行断言 对类型化属性进行断言 每个测试一个巨型断言 每个测试一个逻辑断言 测试名称描述实现 按行为命名: Method_ExpectedResult_WhenCondition 忽略 CancellationToken 始终传递并验证取消 运行测试 # 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/
Agent 识别该技能的关键词,点击任意一个即可复制。
该技能未提供触发词。
下载的 .skill 包内含以下字段。
| 字段 | 说明 |
|---|---|
| format | 格式标识(skill/v1) |
| skill_id | 技能唯一 ID |
| name | 技能名称 |
| version | 版本号 |
| description | 技能描述 |
| category | 所属分类(数组) |
| trigger_words | 触发词列表 |
| tags | 标签列表 |
| source | 来源标识 |
| source_url | 来源链接(本页地址) |
| exported_at | 导出时间(每次下载生成) |
| system_prompt | 系统提示词正文 |
| model_config | 模型参数:provider / model / temperature / max_tokens / top_p |
| examples | 示例 |
| install_guide | 各平台导入说明(Coze / Dify / Claude / 自定义框架) |