Skip to content

Testing

Huy Nguyen edited this page Apr 5, 2025 · 2 revisions

Testing

This guide will help you understand how to write and run tests for Mixcore CMS applications.

Unit Testing

1. Setting Up Unit Tests

Create a new test project:

dotnet new xunit -n MyModule.Tests

Add necessary NuGet packages:

dotnet add package Moq
dotnet add package FluentAssertions
dotnet add package Microsoft.EntityFrameworkCore.InMemory

2. Service Tests

public class ProductServiceTests
{
    private readonly Mock<IProductRepository> _repositoryMock;
    private readonly Mock<ILogger<ProductService>> _loggerMock;
    private readonly ProductService _service;

    public ProductServiceTests()
    {
        _repositoryMock = new Mock<IProductRepository>();
        _loggerMock = new Mock<ILogger<ProductService>>();
        _service = new ProductService(_repositoryMock.Object, _loggerMock.Object);
    }

    [Fact]
    public async Task GetProductAsync_ExistingProduct_ReturnsProduct()
    {
        // Arrange
        var product = new Product { Id = 1, Name = "Test Product" };
        _repositoryMock.Setup(r => r.GetByIdAsync(1))
            .ReturnsAsync(product);

        // Act
        var result = await _service.GetProductAsync(1);

        // Assert
        result.Should().NotBeNull();
        result.Id.Should().Be(1);
        result.Name.Should().Be("Test Product");
    }

    [Fact]
    public async Task GetProductAsync_NonExistingProduct_ThrowsNotFoundException()
    {
        // Arrange
        _repositoryMock.Setup(r => r.GetByIdAsync(1))
            .ReturnsAsync((Product)null);

        // Act & Assert
        await Assert.ThrowsAsync<NotFoundException>(() => 
            _service.GetProductAsync(1));
    }
}

3. Controller Tests

public class ProductsControllerTests
{
    private readonly Mock<IProductService> _serviceMock;
    private readonly ProductsController _controller;

    public ProductsControllerTests()
    {
        _serviceMock = new Mock<IProductService>();
        _controller = new ProductsController(_serviceMock.Object);
    }

    [Fact]
    public async Task Get_ExistingProduct_ReturnsOk()
    {
        // Arrange
        var product = new ProductDto { Id = 1, Name = "Test Product" };
        _serviceMock.Setup(s => s.GetProductAsync(1))
            .ReturnsAsync(product);

        // Act
        var result = await _controller.Get(1);

        // Assert
        var okResult = Assert.IsType<OkObjectResult>(result);
        var returnedProduct = Assert.IsType<ProductDto>(okResult.Value);
        returnedProduct.Id.Should().Be(1);
        returnedProduct.Name.Should().Be("Test Product");
    }
}

Integration Testing

1. Setting Up Integration Tests

public class ProductIntegrationTests : IClassFixture<WebApplicationFactory<Startup>>
{
    private readonly WebApplicationFactory<Startup> _factory;
    private readonly HttpClient _client;

    public ProductIntegrationTests(WebApplicationFactory<Startup> factory)
    {
        _factory = factory;
        _client = _factory.CreateClient();
    }

    [Fact]
    public async Task GetProduct_ReturnsProduct()
    {
        // Arrange
        var productId = 1;

        // Act
        var response = await _client.GetAsync($"/api/products/{productId}");

        // Assert
        response.EnsureSuccessStatusCode();
        var product = await response.Content.ReadAsAsync<ProductDto>();
        product.Should().NotBeNull();
        product.Id.Should().Be(productId);
    }
}

2. Database Integration Tests

public class ProductRepositoryTests
{
    private readonly DbContextOptions<ApplicationDbContext> _options;
    private readonly ApplicationDbContext _context;

    public ProductRepositoryTests()
    {
        _options = new DbContextOptionsBuilder<ApplicationDbContext>()
            .UseInMemoryDatabase(databaseName: "TestDb")
            .Options;

        _context = new ApplicationDbContext(_options);
    }

    [Fact]
    public async Task AddProduct_ProductAddedSuccessfully()
    {
        // Arrange
        var repository = new ProductRepository(_context);
        var product = new Product { Name = "Test Product", Price = 10.99m };

        // Act
        await repository.AddAsync(product);

        // Assert
        var savedProduct = await _context.Products.FirstOrDefaultAsync();
        savedProduct.Should().NotBeNull();
        savedProduct.Name.Should().Be("Test Product");
    }
}

Testing Best Practices

1. Naming Conventions

public class ProductServiceTests
{
    // MethodName_Scenario_ExpectedResult
    [Fact]
    public async Task GetProductAsync_ProductExists_ReturnsProduct()
    {
        // Test implementation
    }

    // MethodName_Scenario_ThrowsException
    [Fact]
    public async Task GetProductAsync_ProductNotFound_ThrowsNotFoundException()
    {
        // Test implementation
    }
}

2. Test Organization

public class ProductServiceTests
{
    public class GetProductAsync
    {
        [Fact]
        public async Task ProductExists_ReturnsProduct()
        {
            // Test implementation
        }

        [Fact]
        public async Task ProductNotFound_ThrowsNotFoundException()
        {
            // Test implementation
        }
    }

    public class CreateProductAsync
    {
        [Fact]
        public async Task ValidProduct_CreatesProduct()
        {
            // Test implementation
        }

        [Fact]
        public async Task InvalidProduct_ThrowsValidationException()
        {
            // Test implementation
        }
    }
}

3. Mocking Dependencies

public class OrderServiceTests
{
    private readonly Mock<IProductRepository> _productRepositoryMock;
    private readonly Mock<IOrderRepository> _orderRepositoryMock;
    private readonly Mock<IPaymentService> _paymentServiceMock;
    private readonly OrderService _service;

    public OrderServiceTests()
    {
        _productRepositoryMock = new Mock<IProductRepository>();
        _orderRepositoryMock = new Mock<IOrderRepository>();
        _paymentServiceMock = new Mock<IPaymentService>();

        _service = new OrderService(
            _productRepositoryMock.Object,
            _orderRepositoryMock.Object,
            _paymentServiceMock.Object);
    }

    [Fact]
    public async Task PlaceOrder_ValidOrder_ProcessesSuccessfully()
    {
        // Arrange
        var product = new Product { Id = 1, Price = 10.99m };
        _productRepositoryMock.Setup(r => r.GetByIdAsync(1))
            .ReturnsAsync(product);

        _paymentServiceMock.Setup(s => s.ProcessPaymentAsync(It.IsAny<decimal>()))
            .ReturnsAsync(new PaymentResult { Success = true });

        // Act
        var result = await _service.PlaceOrderAsync(new OrderRequest
        {
            ProductId = 1,
            Quantity = 2
        });

        // Assert
        result.Success.Should().BeTrue();
        _orderRepositoryMock.Verify(r => r.AddAsync(It.IsAny<Order>()), Times.Once);
    }
}

Running Tests

1. Running All Tests

dotnet test

2. Running Specific Tests

dotnet test --filter "FullyQualifiedName~ProductServiceTests"

3. Running Tests with Coverage

dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover

Continuous Integration

Add test execution to your CI pipeline:

- name: Run Tests
  run: dotnet test --no-restore --verbosity normal

Next Steps

Need Help?

Clone this wiki locally