mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-07-16 09:37:23 +00:00
364 lines
10 KiB
Markdown
364 lines
10 KiB
Markdown
# 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 across backend and frontend:
|
|
|
|
- **API.Specs** - BDD integration tests using Reqnroll (Gherkin), run against
|
|
a live, seeded database
|
|
- **Features.\*.Tests** - One unit test project per backend feature slice
|
|
(`Features.Auth.Tests`, `Features.Breweries.Tests`,
|
|
`Features.UserManagement.Tests`, `Features.Emails.Tests`), covering that
|
|
slice's command/query handlers and its own repository (mocked with Moq /
|
|
DbMocker, no real database required)
|
|
- **Storybook Vitest project** - Browser-based interaction tests for shared
|
|
website stories
|
|
- **Storybook Playwright suite** - Browser checks against Storybook-rendered
|
|
components
|
|
|
|
## Running Tests with Docker (Recommended)
|
|
|
|
The easiest way to run all tests is using Docker Compose, which sets up an
|
|
isolated test environment:
|
|
|
|
```bash
|
|
docker compose --env-file web/.env.test -f web/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
|
|
|
|
```bash
|
|
# List test result files
|
|
ls -la test-results/
|
|
|
|
# View specific test results
|
|
cat test-results/api-specs/results.trx
|
|
cat test-results/Features.Auth.Tests.trx
|
|
cat test-results/Features.Breweries.Tests.trx
|
|
cat test-results/Features.UserManagement.Tests.trx
|
|
cat test-results/Features.Emails.Tests.trx
|
|
```
|
|
|
|
### Clean Up
|
|
|
|
```bash
|
|
# Remove test containers and volumes
|
|
docker compose --env-file web/.env.test -f web/docker-compose.test.yaml down -v
|
|
```
|
|
|
|
## Running Tests Locally
|
|
|
|
You can run individual test projects locally without Docker:
|
|
|
|
### Integration Tests (API.Specs)
|
|
|
|
```bash
|
|
cd web/backend
|
|
dotnet test API/API.Specs/API.Specs.csproj
|
|
```
|
|
|
|
**Requirements**:
|
|
|
|
- SQL Server instance running
|
|
- Database migrated and seeded
|
|
- Environment variables set (DB connection, JWT secret)
|
|
|
|
### Feature Slice Unit Tests
|
|
|
|
Each feature slice has its own test project, covering its command/query
|
|
handlers and repository:
|
|
|
|
```bash
|
|
cd web/backend
|
|
dotnet test Features/Features.Auth.Tests/Features.Auth.Tests.csproj
|
|
dotnet test Features/Features.Breweries.Tests/Features.Breweries.Tests.csproj
|
|
dotnet test Features/Features.UserManagement.Tests/Features.UserManagement.Tests.csproj
|
|
dotnet test Features/Features.Emails.Tests/Features.Emails.Tests.csproj
|
|
|
|
# Or run all of them at once via the solution:
|
|
for proj in Features/*.Tests; do dotnet test "$proj"; done
|
|
```
|
|
|
|
**Requirements**:
|
|
|
|
- No database required (handlers use Moq; repository tests use DbMocker to
|
|
simulate SQL Server responses)
|
|
|
|
### Frontend Storybook Tests
|
|
|
|
```bash
|
|
cd web/frontend
|
|
npm install
|
|
npm run test:storybook
|
|
```
|
|
|
|
**Purpose**:
|
|
|
|
- Verifies shared stories such as form fields, submit buttons, navbar states,
|
|
toasts, and the theme gallery
|
|
- Runs in browser mode via Vitest and Storybook integration
|
|
|
|
### Frontend Playwright Storybook Tests
|
|
|
|
```bash
|
|
cd web/frontend
|
|
npm install
|
|
npm run test:storybook:playwright
|
|
```
|
|
|
|
**Requirements**:
|
|
|
|
- Storybook dependencies installed
|
|
- Playwright browser dependencies installed
|
|
- The command will start or reuse the Storybook server defined in
|
|
`playwright.storybook.config.ts`
|
|
|
|
## Test Coverage
|
|
|
|
### Current Coverage
|
|
|
|
**Features.Auth.Tests**:
|
|
|
|
- User registration with validation
|
|
- User login with JWT token generation
|
|
- Password hashing and verification (Argon2id)
|
|
- Email confirmation and confirmation-email resend
|
|
- Refresh token exchange
|
|
- JWT token generation, validation, and claims handling
|
|
- `IAuthRepository` data access (DbMocker-backed)
|
|
- Invalid credentials and 404 error responses (via API.Specs)
|
|
|
|
**Features.Breweries.Tests**:
|
|
|
|
- Brewery create/update/delete commands and get-by-id/get-all queries
|
|
|
|
**Features.UserManagement.Tests**:
|
|
|
|
- User get-by-id/get-all queries and the (currently unrouted) update command
|
|
|
|
**Features.Emails.Tests**:
|
|
|
|
- Registration and resend-confirmation email dispatch handlers
|
|
|
|
**Frontend UI Coverage**:
|
|
|
|
- Shared submit button states
|
|
- Form field happy path and error presentation
|
|
- Navbar guest, authenticated, and mobile behavior
|
|
- Theme gallery rendering across Biergarten themes
|
|
- Toast interactions and themed notification display
|
|
|
|
### Planned Coverage
|
|
|
|
- [ ] Password reset functionality
|
|
- [ ] Beer post operations
|
|
- [ ] User follow/unfollow
|
|
- [ ] Image upload service
|
|
- [ ] Frontend route integration coverage beyond Storybook stories
|
|
|
|
## 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/
|
|
│ ├── Registration.feature # Registration scenarios
|
|
│ ├── Login.feature # Login scenarios
|
|
│ ├── Confirmation.feature # Email confirmation scenarios
|
|
│ ├── ResendConfirmation.feature # Resend-confirmation scenarios
|
|
│ ├── TokenRefresh.feature # Refresh token scenarios
|
|
│ ├── AccessTokenValidation.feature # Protected endpoint access scenarios
|
|
│ └── NotFound.feature # 404 handling
|
|
├── Steps/
|
|
│ ├── AuthSteps.cs # Step definitions for the Auth features
|
|
│ └── ApiGeneralSteps.cs # Shared/general step definitions
|
|
├── Mocks/
|
|
│ ├── MockEmailDispatcher.cs # Substitutes Features.Emails' IEmailDispatcher
|
|
│ └── MockEmailProvider.cs # Substitutes Infrastructure.Email's IEmailProvider
|
|
└── TestApiFactory.cs # Test server setup (swaps in the mocks above)
|
|
```
|
|
|
|
**Example Feature**:
|
|
|
|
```gherkin
|
|
Feature: User Registration
|
|
As a user
|
|
I want to register
|
|
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
|
|
```
|
|
|
|
### Features.Auth.Tests
|
|
|
|
```
|
|
Features.Auth.Tests/
|
|
├── Commands/
|
|
│ ├── RegisterUserHandlerTests.cs
|
|
│ ├── ConfirmUserHandlerTests.cs
|
|
│ ├── ResendConfirmationEmailHandlerTests.cs
|
|
│ └── RefreshTokenHandlerTests.cs
|
|
├── Queries/
|
|
│ └── LoginHandlerTests.cs
|
|
├── Repository/
|
|
│ └── AuthRepositoryTests.cs # DbMocker-backed repository tests
|
|
└── Services/
|
|
├── TokenServiceRefreshTests.cs
|
|
└── TokenServiceValidationTests.cs
|
|
```
|
|
|
|
Each of the other three slices (`Features.Breweries.Tests`,
|
|
`Features.UserManagement.Tests`, `Features.Emails.Tests`) follows the same
|
|
shape: a `Commands/`/`Queries/` folder with one test file per handler.
|
|
|
|
## Writing Tests
|
|
|
|
### Unit Test Example (xUnit)
|
|
|
|
```csharp
|
|
public class LoginHandlerTests
|
|
{
|
|
[Fact]
|
|
public async Task Handle_WithValidData_ReturnsPayloadWithMatchingUsername()
|
|
{
|
|
// Arrange
|
|
var authRepoMock = new Mock<IAuthRepository>();
|
|
var passwordInfraMock = new Mock<IPasswordInfrastructure>();
|
|
var tokenServiceMock = new Mock<ITokenService>();
|
|
var handler = new LoginHandler(authRepoMock.Object, passwordInfraMock.Object, tokenServiceMock.Object);
|
|
// ...set up mocks for a known user/credential...
|
|
|
|
// Act
|
|
var result = await handler.Handle(new LoginQuery("testuser", "password123"), CancellationToken.None);
|
|
|
|
// Assert
|
|
result.Should().NotBeNull();
|
|
result.AccessToken.Should().NotBeNullOrEmpty();
|
|
}
|
|
}
|
|
```
|
|
|
|
### Integration Test Example (Reqnroll)
|
|
|
|
```gherkin
|
|
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:
|
|
|
|
```bash
|
|
# CI/CD command
|
|
docker compose --env-file web/.env.test -f web/docker-compose.test.yaml build
|
|
docker compose --env-file web/.env.test -f web/docker-compose.test.yaml up --abort-on-container-exit
|
|
docker compose --env-file web/.env.test -f web/docker-compose.test.yaml down -v
|
|
```
|
|
|
|
Exit codes:
|
|
|
|
- `0` - All tests passed
|
|
- Non-zero - Test failures occurred
|
|
|
|
Frontend UI checks should also be included in CI for the active website
|
|
workspace:
|
|
|
|
```bash
|
|
cd web/frontend
|
|
npm ci
|
|
npm run test:storybook
|
|
npm run test:storybook:playwright
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Tests Failing Due to Database Connection
|
|
|
|
Ensure SQL Server is running and environment variables are set:
|
|
|
|
```bash
|
|
docker compose --env-file web/.env.test -f web/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:
|
|
|
|
```bash
|
|
docker compose --env-file web/.env.test -f web/docker-compose.test.yaml down -v
|
|
```
|
|
|
|
### View Container Logs
|
|
|
|
```bash
|
|
docker compose --env-file web/.env.test -f web/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
|