Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="Microsoft.Playwright" Version="1.49.0" />
<PackageReference Include="NUnit" Version="4.4.0" />
<PackageReference Include="NUnit3TestAdapter" Version="6.0.0" />
<PackageReference Include="Reqnroll" Version="3.2.1" />
<PackageReference Include="Reqnroll.NUnit" Version="3.2.1" />
<PackageReference Include="Reqnroll.Tools.MsBuild.Generation" Version="3.2.1" />
<PackageReference Include="Shouldly" Version="4.3.0" />
<PackageReference Include="Testcontainers" Version="4.2.0" />
</ItemGroup>

<ItemGroup>
<Folder Include="Features\" />
<Folder Include="Support\" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using NUnit.Framework;

// Configure NUnit for parallel execution at the Children level
// This allows scenarios to run in parallel, which is ideal for browser-based tests
[assembly: Parallelizable(ParallelScope.Children)]
[assembly: LevelOfParallelism(4)]
196 changes: 196 additions & 0 deletions EstateManagementUI.BlazorServer.Tests.Integration/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
# EstateManagementUI.BlazorServer.Tests.Integration

**Testcontainers-based integration test project** for the Blazor Server UI using Playwright for browser automation and Reqnroll for BDD-style scenarios.

## Overview

This project provides a "zero-process" testing environment where the Blazor application runs in a Docker container and is tested using BDD-style scenarios.

### Key Features

1. **Pre-built Image Testing**: Uses a pre-built Docker image (`estatemanagementui-blazor`) for fast test execution
2. **Testcontainers Integration**: Uses official Testcontainers for .NET to manage full container lifecycle
3. **Browser Automation**: Microsoft Playwright for cross-browser testing (Chromium, Firefox, WebKit)
4. **BDD Framework**: Reqnroll (modern SpecFlow successor) with NUnit test runner
5. **Parallel Execution**: Tests run in parallel at Children level (4 concurrent scenarios)
6. **Test Environment**: Application runs in "Test" mode with isolated data (no external API calls)

## Prerequisites

Before running tests, build the Docker image locally:

```bash
cd EstateManagementUI.BlazorServer
docker build -t estatemanagementui-blazor .
```

## Technical Architecture

### Container Management

The `DockerHelper` class:
- Uses pre-built Docker image `estatemanagementui-blazor`
- Starts container with Test environment configuration
- Manages lifecycle from setup to teardown
- Implements `IAsyncDisposable` for proper resource cleanup

```csharp
// Uses pre-built image
_blazorServerImageName = "estatemanagementui-blazor";

var container = new ContainerBuilder()
.WithImage(_blazorServerImageName)
.WithEnvironment("ASPNETCORE_ENVIRONMENT", "Test")
.WithEnvironment("AppSettings:TestMode", "true")
.Build();
```

### Test Lifecycle (Hooks)

Reqnroll hooks manage the complete test lifecycle:

- **BeforeTestRun (Order 0)**: Start Docker containers from pre-built image
- **BeforeTestRun (Order 100)**: Initialize Playwright
- **BeforeScenario**: Create browser page, register DockerHelper
- **AfterScenario**: Take screenshots on failure, close page
- **AfterTestRun (Order 0)**: Close Playwright
- **AfterTestRun (Order 100)**: Stop containers and cleanup

### Parallel Execution

Configured in `Properties/AssemblyInfo.cs`:

```csharp
[assembly: Parallelizable(ParallelScope.Children)]
[assembly: LevelOfParallelism(4)]
```

## Project Structure

```
EstateManagementUI.BlazorServer.Tests.Integration/
├── Support/
│ ├── DockerHelper.cs # Testcontainers container management
│ └── Hooks.cs # Reqnroll lifecycle hooks
├── Features/
│ └── Framework/
│ ├── FrameworkCheck.feature # Smoke test
│ └── FrameworkCheckSteps.cs # Step definitions
├── Properties/
│ └── AssemblyInfo.cs # NUnit parallel configuration
└── EstateManagementUI.BlazorServer.Tests.Integration.csproj
```

## Running Tests

### Prerequisites

1. **.NET 10.0 SDK** or later
2. **Docker** (Docker Desktop on Windows/Mac, Docker Engine on Linux)
3. **Pre-built Docker image**: Build the image before running tests:
```bash
cd EstateManagementUI.BlazorServer
docker build -t estatemanagementui-blazor .
```

### Quick Start

```bash
# Ensure the Docker image is built first
docker images | grep estatemanagementui-blazor

# Navigate to the test project
cd EstateManagementUI.BlazorServer.Tests.Integration

# Run all tests (uses pre-built image)
dotnet test

# Run only framework smoke tests
dotnet test --filter "Category=framework"

# Run with specific browser
Browser=Firefox dotnet test

# Run in headless mode (CI)
IsCI=true dotnet test
```

### Test Execution Flow

1. **Image Check**: Tests use pre-built `estatemanagementui-blazor` image
2. **Container Start**: Container started with test-specific configuration
3. **Browser Setup**: Playwright initializes configured browser(s)
4. **Test Run**: Tests execute in parallel (up to 4 scenarios)
5. **Cleanup**: Containers stopped, browsers closed, Docker resources cleaned up

### Environment Variables

- **`Browser`**: Browser type (`Chrome`, `Firefox`, `WebKit`) - defaults to Chrome
- **`IsCI`**: Set to `true` for headless mode - defaults to false (headed mode)

## Framework Check Feature

The `FrameworkCheck.feature` validates the infrastructure:

```gherkin
@framework @smoke
Feature: Framework Check
Background:
Given the application is running in a container

Scenario: Home page is accessible
When I navigate to the home page
Then the page title should be visible
```

This test validates:
- ✓ Pre-built Docker image exists and can be used
- ✓ Container starts with proper configuration
- ✓ Playwright connects to containerized application
- ✓ Basic navigation works

## Package Dependencies

- **Testcontainers** 4.2.0 - Official Testcontainers for .NET
- **Microsoft.Playwright** 1.49.0 - Browser automation
- **Reqnroll** 3.2.1 - BDD framework (SpecFlow successor)
- **NUnit** 4.4.0 - Test runner
- **Shouldly** 4.3.0 - Assertion library

## Troubleshooting

### Image Not Found

1. Build the Docker image first:
```bash
cd EstateManagementUI.BlazorServer
docker build -t estatemanagementui-blazor .
```
2. Verify image exists: `docker images | grep estatemanagementui-blazor`

### Container Won't Start

1. Check Docker is running: `docker ps`
2. Check Docker logs: `docker logs <container-name>`
3. Verify port 5004 not already in use
4. Check Docker has sufficient resources

### Playwright Issues

1. Browsers installed automatically in BeforeTestRun
2. Check container accessible: `curl -k https://localhost:<port>`
3. Verify SSL certificate handling configured

## Future Expansion

The project structure supports adding more feature files:

```
Features/
├── Framework/ # Infrastructure tests
├── Authentication/ # Auth flow tests
├── Estates/ # Estate management tests
└── Reporting/ # Reporting feature tests
```

Each feature area can have its own `.feature` files and step definitions.
15 changes: 15 additions & 0 deletions EstateManagementUI.sln
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EstateManagementUI.BlazorSe
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EstateManagementUI.BlazorServer.Tests", "EstateManagementUI.BlazorServer.Tests\EstateManagementUI.BlazorServer.Tests.csproj", "{55431CBE-C879-47B9-9607-A5822DE9B856}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EstateManagementUI.BlazorServer.Tests.Integration", "EstateManagementUI.BlazorServer.Tests.Integration\EstateManagementUI.BlazorServer.Tests.Integration.csproj", "{A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -45,13 +47,26 @@ Global
{55431CBE-C879-47B9-9607-A5822DE9B856}.Release|x64.Build.0 = Release|Any CPU
{55431CBE-C879-47B9-9607-A5822DE9B856}.Release|x86.ActiveCfg = Release|Any CPU
{55431CBE-C879-47B9-9607-A5822DE9B856}.Release|x86.Build.0 = Release|Any CPU
{A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Debug|x64.ActiveCfg = Debug|Any CPU
{A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Debug|x64.Build.0 = Debug|Any CPU
{A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Debug|x86.ActiveCfg = Debug|Any CPU
{A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Debug|x86.Build.0 = Debug|Any CPU
{A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Release|Any CPU.Build.0 = Release|Any CPU
{A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Release|x64.ActiveCfg = Release|Any CPU
{A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Release|x64.Build.0 = Release|Any CPU
{A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Release|x86.ActiveCfg = Release|Any CPU
{A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{C52FE3F3-E999-4A12-A20B-0D5C6DA34AA9} = {05420FAB-1C50-4AAD-9E7C-2A86184019B7}
{55431CBE-C879-47B9-9607-A5822DE9B856} = {E7671C23-F30C-471A-A9EF-AC85DC607B55}
{A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F} = {E7671C23-F30C-471A-A9EF-AC85DC607B55}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C5AE6DA4-B167-44E5-9B63-61C7E8D1BC9F}
Expand Down
Loading