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.
npx -y skills add jzills/claude-marketplace --skill dotnet-unit-testsAssembled 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_ThrowsArgumentExceptionGetUser_WhenUserExists_ReturnsUserDtoProcessOrder_WhenStockIsEmpty_PublishesOutOfStockEventSave_Always_PersistsToRepository(useAlwayswhen 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.NoworGuid.NewGuid()directly — wrap them behind an interface and mock that
Output Format
When generating tests for a class:
- Read the class under test — understand every public method, its parameters, return types, and injected dependencies
- Identify test cases — happy paths, edge cases, error conditions, async behavior, null inputs
- Write a complete, compilable test file — include all
usingstatements, the correct namespace, and[TestFixture]class scaffold - Name the test class
{ClassName}Testsand 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.