xUnit.net testing framework with Fact, Theory, fixtures, DI, and mocking with Moq and NSubstitute. Covers .NET testing best practices. USE WHEN: user mentions "xUnit", ".NET testing", "Fact", "Theory", "InlineData", "Moq", "NSubstitute", "C# unit test" DO NOT USE FOR: NUnit - use `nunit`, Vitest - use `vitest`, Jest - use `jest`, Playwright - use `playwright`

GitHub
Install command
npx skhub add claude-dev-suite/xunit
Markdown
SKILL.md

xUnit.net - Quick Reference

Deep Knowledge: Use mcp__documentation__fetch_docs with technology: xunit for comprehensive documentation.

Basic Tests

public class UserServiceTests
{
    [Fact]
    public async Task GetById_ExistingUser_ReturnsUser()
    {
        // Arrange
        var repository = new Mock<IUserRepository>();
        repository.Setup(r => r.GetByIdAsync(1))
            .ReturnsAsync(new User { Id = 1, Name = "Alice" });

        var service = new UserService(repository.Object, Mock.Of<ILogger<UserService>>());

        // Act
        var result = await service.GetByIdAsync(1);

        // Assert
        Assert.NotNull(result);
        Assert.Equal("Alice", result.Name);
    }

    [Fact]
    public async Task GetById_NonExistingUser_ReturnsNull()
    {
        var repository = new Mock<IUserRepository>();
        repository.Setup(r => r.GetByIdAsync(999)).ReturnsAsync((User?)null);

        var service = new UserService(repository.Object, Mock.Of<ILogger<UserService>>());

        var result = await service.GetByIdAsync(999);

        Assert.Null(result);
    }
}

Theory (Parameterized Tests)

[Theory]
[InlineData("", false)]
[InlineData("a", false)]
[InlineData("ab", true)]
[InlineData("valid@email.com", true)]
public void Validate_Name_ReturnsExpected(string name, bool expected)
{
    var validator = new UserValidator();
    var result = validator.IsValidName(name);
    Assert.Equal(expected, result);
}

[Theory]
[MemberData(nameof(GetTestUsers))]
public async Task Create_ValidUser_Succeeds(CreateUserRequest request)
{
    // Test with complex objects
}

public static IEnumerable<object[]> GetTestUsers()
{
    yield return new object[] { new CreateUserRequest("Alice", "alice@test.com") };
    yield return new object[] { new CreateUserRequest("Bob", "bob@test.com") };
}

Mocking with Moq

// Setup
var mock = new Mock<IUserRepository>();

// Return value
mock.Setup(r => r.GetByIdAsync(It.IsAny<int>()))
    .ReturnsAsync(new User { Id = 1, Name = "Test" });

// Callback
mock.Setup(r => r.AddAsync(It.IsAny<User>()))
    .Callback<User>(user => user.Id = 42)
    .Returns(Task.CompletedTask);

// Verify
mock.Verify(r => r.AddAsync(It.Is<User>(u => u.Name == "Alice")), Times.Once);
mock.Verify(r => r.DeleteAsync(It.IsAny<int>()), Times.Never);

// Sequence
mock.SetupSequence(r => r.GetByIdAsync(1))
    .ReturnsAsync(new User { Name = "First" })
    .ReturnsAsync(new User { Name = "Second" });

Mocking with NSubstitute

var repository = Substitute.For<IUserRepository>();

// Return value
repository.GetByIdAsync(1).Returns(new User { Id = 1, Name = "Test" });

// Verify
await repository.Received(1).AddAsync(Arg.Is<User>(u => u.Name == "Alice"));
await repository.DidNotReceive().DeleteAsync(Arg.Any<int>());

Fixtures (Shared Context)

// Class fixture - shared per test class
public class DatabaseFixture : IAsyncLifetime
{
    public AppDbContext Context { get; private set; } = default!;

    public async Task InitializeAsync()
    {
        var options = new DbContextOptionsBuilder<AppDbContext>()
            .UseInMemoryDatabase(Guid.NewGuid().ToString())
            .Options;
        Context = new AppDbContext(options);
        await Context.Database.EnsureCreatedAsync();
    }

    public async Task DisposeAsync()
    {
        await Context.DisposeAsync();
    }
}

public class UserRepositoryTests : IClassFixture<DatabaseFixture>
{
    private readonly AppDbContext _context;

    public UserRepositoryTests(DatabaseFixture fixture)
    {
        _context = fixture.Context;
    }
}

// Collection fixture - shared across test classes
[CollectionDefinition("Database")]
public class DatabaseCollection : ICollectionFixture<DatabaseFixture> { }

[Collection("Database")]
public class OrderRepositoryTests { }

FluentAssertions (Optional)

// Install: dotnet add package FluentAssertions
result.Should().NotBeNull();
result.Name.Should().Be("Alice");
result.Age.Should().BeGreaterThan(0).And.BeLessThan(150);
users.Should().HaveCount(3).And.OnlyContain(u => u.IsActive);

Anti-Patterns

Anti-PatternWhy It's BadCorrect Approach
Testing implementation detailsBrittle testsTest behavior
Shared mutable stateFlaky testsUse fresh fixtures per test
No assertion messageHard to debugUse descriptive assertions
Testing private methodsCoupling to internalsTest through public API

Quick Troubleshooting

IssueLikely CauseSolution
Test not discoveredMissing [Fact]/[Theory]Add test attribute
Fixture not sharedMissing IClassFixture<T>Implement fixture interface
Mock returns nullMissing setupAdd .Setup() for the call
Async test deadlockMissing async keywordUse async Task return type
Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

MIT

Source path

skills/testing/xunit

Default branch

main

Latest commit

9496306

Tree SHA

fe4e2f1