agentsclimarketplace

Dotnet unit tests

Skill jzills/claude-marketplace/plugins/dotnet-unit-tests/skills/dotnet-unit-tests

A Claude Code plugin marketplace with skills for git workflows, code quality, safety, and more.

Install
npx -y skills add jzills/claude-marketplace --skill dotnet-unit-tests

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use this skill whenever the user asks you to write, generate, add, or scaffold unit tests for C# or .NET code. Trigger on: "write tests for this", "add unit tests", "test this class", "generate tests", "cover this with tests", "write NUnit tests", "mock this dependency", "how do I test this", "add test coverage", "write a test for this method", or any request that involves C#/.NET testing. Also trigger proactively when the user shows you a C# class or method and asks you to improve, review, or complete it — if there are no tests, offer to write them.

SKILL.md

6.4 KB, as published. Nobody here has run it

.NET Unit Test Writer

You are writing production-quality unit tests for C# code using NUnit and Moq. Tests should be readable, maintainable, and actually catch bugs — not just satisfy a coverage metric.


Project Setup

If no test project exists, scaffold one using assets/test-project.csproj as the starting point.


Test Structure: AAA

Every test follows Arrange / Act / Assert. Separate the three sections with blank lines. Skip // Arrange comments unless the test is unusually long — well-named variables make the sections self-evident.

[Test]
public void Withdraw_WhenBalanceSufficient_DeductsAmount()
{
    var account = new BankAccount(initialBalance: 100m);

    account.Withdraw(30m);

    account.Balance.Should().Be(70m);
}

Naming Convention

Use the format: MethodName_Condition_ExpectedOutcome

  • Calculate_WhenInputIsNegative_ThrowsArgumentException
  • GetUser_WhenUserExists_ReturnsUserDto
  • ProcessOrder_WhenStockIsEmpty_PublishesOutOfStockEvent
  • Save_Always_PersistsToRepository (use Always when there is no meaningful condition)

This makes failures self-documenting — the test name alone tells you exactly what broke.


Fixture Setup

NUnit creates a new instance per test, so use [SetUp] for per-test construction and [OneTimeSetUp] only for genuinely expensive shared resources.

[TestFixture]
public class OrderServiceTests
{
    private Mock<IOrderRepository> _repositoryMock;
    private Mock<IEventBus> _eventBusMock;
    private OrderService _sut;

    [SetUp]
    public void SetUp()
    {
        _repositoryMock = new Mock<IOrderRepository>();
        _eventBusMock = new Mock<IEventBus>();
        _sut = new OrderService(_repositoryMock.Object, _eventBusMock.Object);
    }
}

Recreate mocks in [SetUp], not as field initializers — this prevents state leaking between tests.


Mocking with Moq

Mock dependencies that cross a boundary: databases, HTTP clients, clocks, file systems, external services. Do not mock types you own unless they are genuinely expensive or non-deterministic.

[Test]
public async Task PlaceOrder_WhenValid_SavesAndPublishes()
{
    var order = new Order { Id = Guid.NewGuid(), Total = 50m };
    _repositoryMock.Setup(r => r.SaveAsync(order)).Returns(Task.CompletedTask);

    await _sut.PlaceOrderAsync(order);

    _repositoryMock.Verify(r => r.SaveAsync(order), Times.Once);
    _eventBusMock.Verify(e => e.PublishAsync(It.IsAny<OrderPlacedEvent>()), Times.Once);
}

Common Moq patterns: See references/moq-patterns.md for setup examples (return values, nulls, exceptions, argument matching, and capture).


Parameterized Tests

Use [TestCase] to cover boundary conditions and multiple inputs without duplicating test bodies.

[TestCase(0)]
[TestCase(-1)]
[TestCase(int.MinValue)]
public void Divide_WhenDivisorIsInvalid_ThrowsArgumentException(int divisor)
{
    var calc = new Calculator();

    Action act = () => calc.Divide(10, divisor);

    act.Should().Throw<ArgumentException>();
}

Use [TestCaseSource] when the inputs are complex objects or need to be built programmatically.


Assertions with FluentAssertions

FluentAssertions produces failure messages that tell you exactly what went wrong, which saves significant debugging time.

// Value equality
result.Should().Be(42);
result.Should().NotBe(0);

// Nullability
result.Should().BeNull();
result.Should().NotBeNull();

// Collections
items.Should().HaveCount(3);
items.Should().Contain(item => item.IsActive);
items.Should().BeEquivalentTo(expected); // deep equality, order-insensitive

// Strings
name.Should().StartWith("Order_").And.HaveLength(12);

// Exceptions (sync)
Action act = () => _sut.Process(null);
act.Should().Throw<ArgumentNullException>().WithMessage("*parameter*");

// Exceptions (async)
Func<Task> act = () => _sut.ProcessAsync(order);
await act.Should().ThrowAsync<InvalidOperationException>().WithMessage("*out of stock*");

Async Tests

Always await async methods under test. NUnit supports async Task test methods natively.

[Test]
public async Task GetUserAsync_WhenNotFound_ReturnsNull()
{
    _repositoryMock.Setup(r => r.FindByIdAsync(99)).ReturnsAsync((User?)null);

    var result = await _sut.GetUserAsync(99);

    result.Should().BeNull();
}

Code Style

Lambda parameters: Use the type name (singular) rather than x. This makes the intent clear without needing to look up what x refers to.

// Preferred
items.Should().Contain(item => item.IsActive);
_mock.Setup(repo => repo.SaveAsync(It.IsAny<Entity>()));

// Avoid
items.Should().Contain(x => x.IsActive);
_mock.Setup(x => x.SaveAsync(It.IsAny<Entity>()));

What NOT to Do

  • Do not test private methods directly — test them through the public API
  • Do not assert on internal implementation details unless verifying observable side effects
  • Do not use Thread.Sleep — if timing matters, inject a clock abstraction (IClock) and mock it
  • Do not share mutable mock state between tests — always reset in [SetUp]
  • Do not write a test that can never fail
  • Do not mock DateTime.Now or Guid.NewGuid() directly — wrap them behind an interface and mock that

Output Format

When generating tests for a class:

  1. Read the class under test — understand every public method, its parameters, return types, and injected dependencies
  2. Identify test cases — happy paths, edge cases, error conditions, async behavior, null inputs
  3. Write a complete, compilable test file — include all using statements, the correct namespace, and [TestFixture] class scaffold
  4. Name the test class {ClassName}Tests and place it in the same namespace as the class under test, plus .Tests

Always output the full file, not isolated snippets, unless the user explicitly asks for just one test.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.