Zum Inhalt springen

testing-dotnet

Type Skill
Plugin awl-testing · v0.0.27
Invoke /awl-testing:testing-dotnet
Tools Read, Write, Edit, Bash, Glob, Grep
Source plugins/awl-testing/skills/testing-dotnet/SKILL.md

Writes and debugs .NET tests using xUnit, Moq, FluentAssertions, and Testcontainers. Use when “write dotnet tests”, “xunit test”, “mock with moq”, “test c# code”, “fluentassertions”, “testcontainers”, “integration test c#”, “api test dotnet”, “fix dotnet test”, “debug test failure”, “test fixture setup”, “theory test”, “fact test”.

Trigger phrases: write dotnet tests · xunit test · mock with moq · test c# code · fluentassertions · testcontainers · integration test c# · api test dotnet · fix dotnet test · debug test failure · test fixture setup · theory test · fact test

[Fact]
public void CreateUser_WithoutEmail_ShouldThrow()
{
// Arrange
var service = new UserService();
// Act
Action act = () => service.CreateUser(new UserData { Name = "Alice" });
// Assert
act.Should().Throw<ArgumentException>().WithMessage("*email*");
}

Run tests:

Terminal-Fenster
dotnet test
dotnet test --filter "FullyQualifiedName~UserService"
Task This Skill Helps With
Unit tests Testing isolated components with mocks
Integration tests Testing with databases, EF Core
API tests Testing ASP.NET Core endpoints
Test failures Debugging red tests, fixing assertions
Test setup Fixtures, lifecycle, IAsyncLifetime

Classes: <TypeUnderTest>Tests (e.g., UserServiceTests)

Methods: <Method>_<State>_<Expected> (e.g., CreateUser_WithoutEmail_ShouldThrow)

Projects: Tests.Unit, Tests.Integration, Tests.Api

[Fact]
public async Task GetUserById_ExistingUser_ReturnsUser()
{
// Arrange
var mockRepo = new Mock<IUserRepository>();
mockRepo.Setup(r => r.GetByIdAsync(1))
.ReturnsAsync(new User { Id = 1, Name = "John" });
var service = new UserService(mockRepo.Object);
// Act
var user = await service.GetUserByIdAsync(1);
// Assert
user.Should().NotBeNull();
user.Name.Should().Be("John");
mockRepo.Verify(r => r.GetByIdAsync(1), Times.Once);
}
[Theory]
[InlineData(2, 3, 5)]
[InlineData(-1, 1, 0)]
[InlineData(0, 0, 0)]
public void Add_ReturnsExpectedResult(int a, int b, int expected)
{
// Arrange
var calc = new Calculator();
// Act
var result = calc.Add(a, b);
// Assert
result.Should().Be(expected);
}
[Fact]
public void Process_NullInput_ThrowsArgumentNull()
{
// Arrange
var service = new MyService();
// Act
Action act = () => service.ProcessData(null);
// Assert
act.Should().Throw<ArgumentNullException>().WithMessage("*data*");
}
[Fact]
public async Task ProcessAsync_ValidInput_Succeeds()
{
// Arrange
var service = new MyService();
// Act
var result = await service.ProcessAsync(validData);
// Assert
result.Should().NotBeNull();
}
public class UserServiceTests : IDisposable
{
private readonly UserService _service;
private readonly Mock<IUserRepository> _mockRepo;
public UserServiceTests()
{
_mockRepo = new Mock<IUserRepository>();
_service = new UserService(_mockRepo.Object);
}
public void Dispose() => _service?.Dispose();
}
public class DatabaseTests : IAsyncLifetime
{
private TestDatabase _db;
public async Task InitializeAsync()
{
_db = new TestDatabase();
await _db.ConnectAsync();
}
public async Task DisposeAsync() => await _db.CleanupAsync();
}
public class DatabaseFixture : IAsyncLifetime
{
public DbContext DbContext { get; private set; }
public async Task InitializeAsync()
{
DbContext = await CreateDbContextAsync();
await DbContext.Database.MigrateAsync();
}
public async Task DisposeAsync() => await DbContext.DisposeAsync();
}
public class MyTests : IClassFixture<DatabaseFixture>
{
private readonly DatabaseFixture _fixture;
public MyTests(DatabaseFixture fixture) => _fixture = fixture;
}
result.Should().Be(expected);
result.Should().NotBeNull();
list.Should().NotBeEmpty();
list.Should().HaveCount(3);
list.Should().Contain(item);
list.Should().OnlyContain(x => x.IsActive);
str.Should().StartWith("Hello");
str.Should().Contain("world");
Terminal-Fenster
# Check if discovered
dotnet test --list-tests
# Rebuild
dotnet clean && dotnet build
# Check filter
dotnet test --filter "FullyQualifiedName~ClassName"
// BAD: Blocks thread
var result = service.GetAsync().Result;
// GOOD: Await properly
var result = await service.GetAsync();
// BAD: Wrong setup
mockRepo.Setup(r => r.GetById(1));
// GOOD: Return value
mockRepo.Setup(r => r.GetById(1)).Returns(user);
mockRepo.Setup(r => r.GetByIdAsync(1)).ReturnsAsync(user);
Terminal-Fenster
dotnet add package FluentAssertions

Then add using FluentAssertions; to test file.

  1. AAA Pattern - Always Arrange-Act-Assert with comments
  2. One assertion concept - Test one behavior per method
  3. Descriptive names - Method name explains the scenario
  4. Independent tests - No shared state, no order dependency
  5. Async all the way - Never .Result or .Wait()
  6. FluentAssertions - More readable than xUnit assertions
  7. Fakes over mocks - When simpler implementations work
Terminal-Fenster
# All tests
dotnet test
# Specific project
dotnet test Tests.Unit/Tests.Unit.csproj
# Filter by name
dotnet test --filter "FullyQualifiedName~UserService"
# With coverage
dotnet test /p:CollectCoverage=true
# Verbose output
dotnet test --logger:"console;verbosity=detailed"

For advanced patterns and complete examples: