Skip to main content

Continuous integration

A CI run should leave more than a red or green line. Keep the runner result, HTML report and .prototrace together: the runner says which test failed, the report shows coverage and findings across the run, and the trace explains where the scenario diverged.

Put every artifact in one place

Relative output paths resolve below the test project's build output. That is convenient locally, but forces CI to search through bin/**. Give CI one absolute directory instead:

var results = Environment.GetEnvironmentVariable("PROTOTEST_RESULTS")
?? Path.Combine("TestResults", "ProtoTest");

builder
.ConfigureTracing(trace =>
trace.OutputPath = Path.Combine(results, "run.prototrace"))
.AddSink<JsonReportSink>(sink =>
sink.OutputPath = Path.Combine(results, "report.json"))
.AddSink<HtmlReportSink>(sink =>
sink.OutputPath = Path.Combine(results, "report.html"));

The CI configuration below sets PROTOTEST_RESULTS to its artifact directory. Locally, the same setup continues to write below TestResults/ProtoTest.

Treat traces as test output

A trace can contain sanitized requests, response bodies, state values and attachments. ProtoTest removes authorization headers and browser input values, but your own attributes and attachments may still carry application data. Use private build artifacts where the suite touches private data, and apply the same retention policy as other test results.

GitHub Actions

name: Integration tests

on:
push:
pull_request:

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
env:
PROTOTEST_RESULTS: ${{ github.workspace }}/TestResults/ProtoTest

steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.0.x

- run: dotnet restore
- run: dotnet test --configuration Release --no-restore

- name: Keep ProtoTest evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: prototest-results
path: TestResults/ProtoTest
if-no-files-found: error

if: always() matters: the trace is most useful when the test step failed. GitHub-hosted Linux runners have Docker available for Testcontainers-based infrastructure.

Playwright on Linux

When the suite uses the bundled Playwright browsers, build once, install the browser and its Linux dependencies, then test without rebuilding:

- run: dotnet build --configuration Release --no-restore

- name: Install Chromium
shell: pwsh
run: |
$installer = Get-ChildItem -Recurse -Filter playwright.ps1 |
Where-Object FullName -Match 'bin/Release' |
Select-Object -First 1
if (-not $installer) { throw "playwright.ps1 was not generated." }
pwsh $installer.FullName install --with-deps chromium

- run: dotnet test --configuration Release --no-build --no-restore

If InstallBrowsers = true is enabled in ProtoTest, it downloads a missing browser binary itself. The explicit CI step remains useful on Linux because Playwright also installs the operating-system libraries the browser needs.

Azure Pipelines

trigger:
- main

pool:
vmImage: ubuntu-latest

variables:
PROTOTEST_RESULTS: $(Build.ArtifactStagingDirectory)/ProtoTest

steps:
- task: UseDotNet@2
inputs:
packageType: sdk
version: 10.x

- task: DotNetCoreCLI@2
inputs:
command: test
arguments: --configuration Release

- task: PublishPipelineArtifact@1
condition: succeededOrFailed()
inputs:
targetPath: $(PROTOTEST_RESULTS)
artifact: prototest-results

Use succeededOrFailed() for the same reason as GitHub's always(): publish the evidence even when a test fails.

GitLab CI

integration-tests:
image: mcr.microsoft.com/dotnet/sdk:10.0
variables:
PROTOTEST_RESULTS: "$CI_PROJECT_DIR/TestResults/ProtoTest"
script:
- dotnet restore
- dotnet test --configuration Release --no-restore
artifacts:
when: always
paths:
- TestResults/ProtoTest/
expire_in: 14 days

A container-backed suite also needs a Docker-capable GitLab runner. Whether Docker-in-Docker is allowed and which service configuration is required belongs to the runner installation; ProtoTest only needs the Docker endpoint to be reachable.

What to keep

ArtifactKeep whenWhy
.prototraceAlways; shorter retention for sensitive suitesComplete execution story and attachments
HTML reportAlwaysHuman-readable run, coverage, gates and findings
JSON reportWhen another job consumes itMachine-readable run summary
Runner output (.trx, NUnit XML, JUnit XML)AlwaysNative CI test history and annotations
Playwright trace and screenshotsOn failure, or for a short retention periodBrowser-specific diagnostics carried inside .prototrace and by the runner

Open a downloaded .prototrace in the ProtoTrace viewer. The file is read locally in the browser and is not uploaded to the viewer.