Skip to content

Playwright Integration Testing

Demonstrates browser-based integration testing using Playwright with Aspire's DistributedApplicationTestingBuilder to spin up a complete distributed application for end-to-end testing.

๐Ÿ“‹ Overview

This example shows how to:

  • Set up Playwright for browser automation in .NET integration tests
  • Use DistributedApplicationTestingBuilder to orchestrate a full-stack app during tests
  • Test a React + Vite frontend communicating with a .NET API
  • Generate TypeScript API clients using Kiota from TypeSpec definitions

๐Ÿ—๏ธ Architecture

flowchart LR
    subgraph TestBuilder["DistributedApplicationTestingBuilder"]
        Playwright["Playwright<br/>(Browser)"] --> Frontend["React + Vite<br/>Frontend<br/>(Kiota TS)"]
        Frontend --> API["Weather API<br/>(.NET 10)<br/>(OpenAPI)"]
    end

๐Ÿงฉ Components

Project Description
aspire-playwright.AppHost Aspire orchestration for API and frontend
aspire-playwright.Api .NET 10 Weather API with built-in OpenAPI
aspire-playwright.Frontend React + Vite frontend with Kiota TypeScript client
aspire-playwright.ServiceDefaults OpenTelemetry and health check configuration
aspire-playwright.Tests Playwright integration tests
aspire-playwright.TypeSpec TypeSpec API definition generating OpenAPI

๐Ÿ”‘ Key Features

  • Full-Stack Integration Tests: Spin up complete distributed apps for browser testing
  • Playwright Browser Automation: Real browser testing with Chromium
  • Aspire 13 AddJavaScriptApp: Modern JavaScript/TypeScript app orchestration
  • .NET 10 OpenAPI: Built-in AddOpenApi() and MapOpenApi() (no Swashbuckle)
  • Kiota TypeScript Client: Type-safe API calls from React frontend
  • TypeSpec API Definition: Contract-first API design

๐Ÿ“‚ Project Structure

foundry/dotnet/aspire-playwright/
โ”œโ”€โ”€ aspire-playwright.sln
โ”œโ”€โ”€ Directory.Build.props             # Shared build properties
โ”œโ”€โ”€ Directory.Packages.props          # Central package management
โ”œโ”€โ”€ aspire-playwright.AppHost/
โ”‚   โ”œโ”€โ”€ Program.cs                    # Aspire orchestration
โ”‚   โ””โ”€โ”€ Properties/launchSettings.json
โ”œโ”€โ”€ aspire-playwright.Api/
โ”‚   โ””โ”€โ”€ Program.cs                    # Weather API with OpenAPI
โ”œโ”€โ”€ aspire-playwright.Frontend/
โ”‚   โ”œโ”€โ”€ src/
โ”‚   โ”‚   โ”œโ”€โ”€ App.tsx                   # Main React component
โ”‚   โ”‚   โ””โ”€โ”€ client/                   # Kiota-generated TS client
โ”‚   โ”œโ”€โ”€ package.json
โ”‚   โ””โ”€โ”€ vite.config.ts
โ”œโ”€โ”€ aspire-playwright.ServiceDefaults/
โ”‚   โ””โ”€โ”€ Extensions.cs                 # OpenTelemetry configuration
โ”œโ”€โ”€ aspire-playwright.Tests/
โ”‚   โ””โ”€โ”€ WeatherAppTests.cs            # Playwright integration tests
โ””โ”€โ”€ aspire-playwright.TypeSpec/
    โ””โ”€โ”€ main.tsp                      # TypeSpec API definition

๐Ÿš€ Getting Started

Prerequisites

  • .NET 10.0 SDK
  • Node.js 22.x or 24.x
  • Playwright browsers (auto-installed on first test run)

Running the Application

cd foundry/dotnet/aspire-playwright
dotnet run --project aspire-playwright.AppHost

Running the Tests

cd foundry/dotnet/aspire-playwright
dotnet test

On first run, Playwright will download the required browser (Chromium).

๐Ÿงช Test Examples

The test suite includes 10 integration tests:

Test Description
WeatherTable_Should_Display_5_Records Verifies API returns exactly 5 weather records
RefreshButton_Should_Update_Weather_Data Verifies refresh button fetches new random data
WeatherTable_Should_Have_Correct_Columns Validates table headers (Date, ยฐC, ยฐF, Summary)
MultipleRefreshes_Should_Track_Refresh_Count Tests refresh counter increments correctly
Temperature_Conversion_Should_Be_Correct Validates ยฐF = 32 + (ยฐC / 0.5556) formula
WeatherData_Should_Have_Valid_Summaries Validates summaries from known set
WeatherData_Should_Have_Valid_Date_Format Validates dates are parseable and in range
Page_Should_Have_Correct_Title_And_Heading Verifies page title and h1 heading
LoadingState_Should_Display_While_Fetching Tests loading indicator visibility
Temperature_Range_Should_Be_Valid Validates temperatures within API range

Test Pattern

public class WeatherAppTests : IAsyncLifetime
{
    private DistributedApplication? _app;
    private IPlaywright? _playwright;
    private IBrowser? _browser;

    public async Task InitializeAsync()
    {
        // Start the distributed application
        var appHost = await DistributedApplicationTestingBuilder
            .CreateAsync<Projects.aspire_playwright_AppHost>();
        _app = await appHost.BuildAsync();
        await _app.StartAsync();

        // Initialize Playwright
        _playwright = await Playwright.CreateAsync();
        _browser = await _playwright.Chromium.LaunchAsync();
    }

    [Fact]
    public async Task WeatherTable_Should_Display_5_Records()
    {
        var page = await _browser!.NewPageAsync();
        await page.GotoAsync(_frontendUrl!);

        var rows = await page.Locator("[data-testid='weather-table'] tbody tr")
            .CountAsync();

        Assert.Equal(5, rows);
    }
}

๐Ÿ”ง Key Implementation Details

AppHost Configuration (Aspire 13)

var builder = DistributedApplication.CreateBuilder(args);

var weatherApi = builder.AddProject<Projects.aspire_playwright_Api>("weather-api");

builder.AddJavaScriptApp("frontend", "../aspire-playwright.Frontend", "dev")
    .WithEnvironment("VITE_API_URL", weatherApi.GetEndpoint("http"))
    .WithHttpEndpoint(env: "PORT")
    .WaitFor(weatherApi);

builder.Build().Run();

.NET 10 OpenAPI (No Swashbuckle)

builder.Services.AddOpenApi();

app.MapOpenApi();  // Serves at /openapi/v1.json

Kiota TypeScript Client

import { WeatherApiClient } from './generated/weatherApiClient';
import { FetchRequestAdapter } from '@microsoft/kiota-http-fetchlibrary';

const adapter = new FetchRequestAdapter(new AnonymousAuthenticationProvider());
adapter.baseUrl = import.meta.env.VITE_API_URL;

const client = new WeatherApiClient(adapter);
const forecasts = await client.weatherforecast.get();

๐Ÿ“ก API Endpoints

Endpoint Method Description
/weatherforecast GET Returns 5 random weather items
/openapi/v1.json GET OpenAPI specification

๐Ÿ“ Source Code

Location: foundry/dotnet/aspire-playwright/