Tutorials ASP.NET Core Complete Tutorial (ShopNest)
Unit Testing ASP.NET Core with xUnit and Moq
Learn Unit Testing ASP.NET Core with xUnit and Moq in our free ASP.NET Core Complete Tutorial (ShopNest) series. Step-by-step explanations, examples, and interview tips on Toolliyo Academy.
On this page
Introduction
ShopNest Order Service logic — discounts, stock checks, totals — belongs in unit tests. One hour writing tests saves ten hours debugging production orders at 2 AM. This lesson uses xUnit and Moq with Arrange-Act-Assert on real order scenarios.
After this article you will
- Write [Fact] and [Theory] tests with xUnit
- Mock dependencies with Moq Setup and Verify
- Test services, validators, and controllers in isolation
- Follow naming conventions and AAA pattern
- Understand code coverage basics and TDD intro
Prerequisites
- Article 53 — gRPC with ASP.NET Core
- ShopNest Order Service / API from prior modules
Concept deep-dive
Why unit test?
ROI: catch regressions before deploy; document expected behavior; enable fearless refactoring. Indian service companies increasingly expect xUnit in interviews.
// Naming: MethodName_Scenario_ExpectedBehavior
public class OrderServiceTests
{
[Fact]
public async Task CreateOrderAsync_EmptyCart_ThrowsValidationException()
{
// Arrange
var mockRepo = new Mock<IOrderRepository>();
var svc = new OrderService(mockRepo.Object, Mock.Of<ILogger<OrderService>>());
// Act & Assert
await Assert.ThrowsAsync<ValidationException>(() =>
svc.CreateOrderAsync(new CreateOrderDto { Lines = new() }, "user1"));
mockRepo.Verify(r => r.AddAsync(It.IsAny<Order>(), default), Times.Never);
}
[Theory]
[InlineData(0, false)]
[InlineData(1, true)]
[InlineData(100, true)]
public void IsValidQuantity_ReturnsExpected(int qty, bool expected)
{
Assert.Equal(expected, OrderRules.IsValidQuantity(qty));
}
}
Moq: Setup defines behavior; Verify asserts calls; ReturnsAsync for async methods.
Hands-on — ShopNest E-Commerce Order Service
- ShopNest.OrderService.Tests xUnit project.
- Tests: valid order, empty cart, insufficient stock, discount cap.
- Mock IProductRepository returns stock levels.
- Test FluentValidation CreateOrderValidator rules.
- dotnet test --collect:"XPlat Code Coverage".
[Fact]
public async Task CreateOrderAsync_ValidCart_ReturnsOrderId()
{
var mockProducts = new Mock<IProductRepository>();
mockProducts.Setup(p => p.GetStockAsync(1, default)).ReturnsAsync(10);
var mockOrders = new Mock<IOrderRepository>();
mockOrders.Setup(o => o.AddAsync(It.IsAny<Order>(), default))
.Callback<Order, CancellationToken>((order, _) => order.Id = Guid.NewGuid());
var svc = new OrderService(mockOrders.Object, mockProducts.Object);
var id = await svc.CreateOrderAsync(validDto, "cust-1");
Assert.NotEqual(Guid.Empty, id);
}
Common errors & best practices
- Testing implementation details (private methods) — test public behavior.
- Over-mocking — mock boundaries (repos), not value objects.
- Flaky tests depending on DateTime.Now — inject IClock or freeze time.
- No Assert on exception type — use ThrowsAsync with specific type.
Interview questions
Q: [Fact] vs [Theory]?
A: Fact single case; Theory runs multiple InlineData rows.
Q: Mock vs Stub?
A: Mock verifies interactions; stub only returns canned data.
Q: What to unit test?
A: Business logic in services — not EF or HTTP pipeline (integration tests).
Q: AAA pattern?
A: Arrange setup, Act execute, Assert verify outcome.
Summary
- Unit tests protect ShopNest order business rules
- Moq isolates services from database and HTTP
- Theory tests parameterized edge cases efficiently
- Coverage guides gaps — 80% on core domain is a common target
Previous: gRPC with ASP.NET Core
Next: Integration Testing
FAQ
NUnit vs xUnit?
xUnit is default for modern .NET; parallel by default.
Test controllers?
Prefer testing services; controller tests often thin integration tests.
Sign in to ask a question or upvote helpful answers.
No questions yet — be the first to ask!