Files
the-biergarten-app/docs/testing.md
2026-02-15 21:13:07 -05:00

6.7 KiB

Testing

This document describes the testing strategy and how to run tests for The Biergarten App.

Overview

The project uses a multi-layered testing approach:

  • API.Specs - BDD integration tests using Reqnroll (Gherkin)
  • Infrastructure.Repository.Tests - Unit tests for data access layer
  • Service.Auth.Tests - Unit tests for authentication business logic

The easiest way to run all tests is using Docker Compose, which sets up an isolated test environment:

docker compose -f docker-compose.test.yaml up --abort-on-container-exit

This command:

  1. Starts a fresh SQL Server instance
  2. Runs database migrations
  3. Seeds test data
  4. Executes all test suites in parallel
  5. Exports results to ./test-results/
  6. Exits when tests complete

View Test Results

# List test result files
ls -la test-results/

# View specific test results
cat test-results/api-specs/results.trx
cat test-results/repository-tests/results.trx
cat test-results/service-auth-tests/results.trx

Clean Up

# Remove test containers and volumes
docker compose -f docker-compose.test.yaml down -v

Running Tests Locally

You can run individual test projects locally without Docker:

Integration Tests (API.Specs)

cd src/Core
dotnet test API/API.Specs/API.Specs.csproj

Requirements:

  • SQL Server instance running
  • Database migrated and seeded
  • Environment variables set (DB connection, JWT secret)

Repository Tests

cd src/Core
dotnet test Infrastructure/Infrastructure.Repository.Tests/Infrastructure.Repository.Tests.csproj

Requirements:

  • SQL Server instance running (uses mock data)

Service Tests

cd src/Core
dotnet test Service/Service.Auth.Tests/Service.Auth.Tests.csproj

Requirements:

  • No database required (uses Moq for mocking)

Test Coverage

Current Coverage

Authentication & User Management:

  • User registration with validation
  • User login with JWT token generation
  • Password hashing and verification (Argon2id)
  • JWT token generation and claims
  • Invalid credentials handling
  • 404 error responses

Repository Layer:

  • User account creation
  • User credential management
  • GetUserByUsername queries
  • Stored procedure execution

Service Layer:

  • Login service with password verification
  • Register service with validation
  • Business logic for authentication flow

Planned Coverage

  • Email verification workflow
  • Password reset functionality
  • Token refresh mechanism
  • Brewery data management
  • Beer post operations
  • User follow/unfollow
  • Image upload service

Testing Frameworks & Tools

xUnit

  • Primary unit testing framework
  • Used for Repository and Service layer tests
  • Supports parallel test execution

Reqnroll (Gherkin/BDD)

  • Behavior-driven development framework
  • Used for API integration tests
  • Human-readable test scenarios in .feature files

FluentAssertions

  • Expressive assertion library
  • Makes test assertions more readable
  • Used across all test projects

Moq

  • Mocking framework for .NET
  • Used in Service layer tests
  • Enables isolated unit testing

DbMocker

  • Database mocking for repository tests
  • Simulates SQL Server responses
  • No real database required for unit tests

Test Structure

API.Specs (Integration Tests)

API.Specs/
├── Features/
│   ├── Authentication.feature          # Login/register scenarios
│   └── UserManagement.feature          # User CRUD scenarios
├── Steps/
│   ├── AuthenticationSteps.cs          # Step definitions
│   └── UserManagementSteps.cs
└── Mocks/
    └── TestApiFactory.cs               # Test server setup

Example Feature:

Feature: User Authentication
  As a user
  I want to register and login
  So that I can access the platform

Scenario: Successful user registration
  Given I have valid registration details
  When I register a new account
  Then I should receive a JWT token
  And my account should be created

Infrastructure.Repository.Tests

Infrastructure.Repository.Tests/
├── AuthRepositoryTests.cs              # Auth repository tests
├── UserAccountRepositoryTests.cs       # User account tests
└── TestFixtures/
    └── DatabaseFixture.cs              # Shared test setup

Service.Auth.Tests

Service.Auth.Tests/
├── LoginService.test.cs                # Login business logic tests
└── RegisterService.test.cs             # Registration business logic tests

Writing Tests

Unit Test Example (xUnit)

public class LoginServiceTests
{
    [Fact]
    public async Task LoginAsync_ValidCredentials_ReturnsToken()
    {
        // Arrange
        var mockRepo = new Mock<IAuthRepository>();
        var mockJwt = new Mock<IJwtService>();
        var service = new AuthService(mockRepo.Object, mockJwt.Object);

        // Act
        var result = await service.LoginAsync("testuser", "password123");

        // Assert
        result.Should().NotBeNull();
        result.Token.Should().NotBeNullOrEmpty();
    }
}

Integration Test Example (Reqnroll)

Scenario: User login with valid credentials
  Given a registered user with username "testuser"
  When I POST to "/api/auth/login" with valid credentials
  Then the response status should be 200
  And the response should contain a JWT token

Continuous Integration

Tests run automatically in CI/CD pipelines using the test Docker Compose configuration:

# CI/CD command
docker compose -f docker-compose.test.yaml build
docker compose -f docker-compose.test.yaml up --abort-on-container-exit
docker compose -f docker-compose.test.yaml down -v

Exit codes:

  • 0 - All tests passed
  • Non-zero - Test failures occurred

Troubleshooting

Tests Failing Due to Database Connection

Ensure SQL Server is running and environment variables are set:

docker compose -f docker-compose.test.yaml ps

Port Conflicts

If port 1433 is in use, stop other SQL Server instances or modify the port in docker-compose.test.yaml.

Stale Test Data

Clean up test database:

docker compose -f docker-compose.test.yaml down -v

View Container Logs

docker compose -f docker-compose.test.yaml logs <service-name>

Best Practices

  1. Isolation: Each test should be independent and not rely on other tests
  2. Cleanup: Use fixtures and dispose patterns for resource cleanup
  3. Mocking: Mock external dependencies in unit tests
  4. Descriptive Names: Use clear, descriptive test method names
  5. Arrange-Act-Assert: Follow AAA pattern in unit tests
  6. Given-When-Then: Follow GWT pattern in BDD scenarios