Skip to content
Draft
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
31 changes: 31 additions & 0 deletions src/frontend/src/content/docs/testing/manage-app-host.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import Pivot from '@components/Pivot.astro';
{ id: "xunit", title: "xUnit.net" },
{ id: "mstest", title: "MSTest" },
{ id: "nunit", title: "NUnit" },
{ id: "tunit", title: "TUnit" },
]}
/>

Expand Down Expand Up @@ -120,6 +121,36 @@ public class WebTests
}
```

</Pivot>
<Pivot id="tunit">

With TUnit, you use the hooks [Before](https://tunit.dev/docs/test-lifecycle/setup) and [After](https://tunit.dev/docs/test-lifecycle/cleanup) attributes on methods of the test class to provide the setup and cleanup of the AppHost instance. The `Setup` method is used to create the AppHost instance before the tests are run and the `Cleanup` method disposes the AppHost instance once the tests are completed.

```csharp
public class WebTests
{
private DistributedApplication _app;

[Before(Test)]
public async Task Setup()
{
var appHost = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.AspireApp_AppHost>();

_app = await appHost.BuildAsync();
}

[After(Test)]
public async Task Cleanup() => await _app.DisposeAsync();

[Test]
public async Task GetWebResourceRootReturnsOkStatusCode()
{
// test code here
}
}
```

</Pivot>

By capturing the AppHost in a field when the test run is started, you can access it in each test without the need to recreate it, decreasing the time it takes to run the tests. Then, when the test run completes, the AppHost is disposed, which cleans up any resources that were created during the test run, such as containers.
Expand Down
193 changes: 193 additions & 0 deletions src/frontend/src/content/docs/testing/write-your-first-test.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import Pivot from '@components/Pivot.astro';
{ id: "xunit", title: "xUnit.net" },
{ id: "mstest", title: "MSTest" },
{ id: "nunit", title: "NUnit" },
{ id: "tunit", title: "TUnit" },
]}
/>

Expand Down Expand Up @@ -50,6 +51,13 @@ dotnet new aspire-mstest -o MSTest.Tests
dotnet new aspire-nunit -o NUnit.Tests
```

</Pivot>
<Pivot id="tunit">

```bash
dotnet new aspire-tunit -o TUnit.Tests
```

</Pivot>

Change directory to the newly created test project:
Expand All @@ -74,6 +82,13 @@ cd MSTest.Tests
cd NUnit.Tests
```

</Pivot>
<Pivot id="tunit">

```bash
cd TUnit.Tests
```

</Pivot>

After adding the test project to your Aspire solution, add a project reference to the target AppHost. For example, if your Aspire solution contains an AppHost project named `AspireApp.AppHost`, add a project reference to it from the test project:
Expand All @@ -98,6 +113,13 @@ dotnet reference add ../AspireApp.AppHost/AspireApp.AppHost.csproj --project MST
dotnet reference add ../AspireApp.AppHost/AspireApp.AppHost.csproj --project NUnit.Tests.csproj
```

</Pivot>
<Pivot id="tunit">

```bash
dotnet reference add ../AspireApp.AppHost/AspireApp.AppHost.csproj --project TUnit.Tests.csproj
```

</Pivot>

Finally, you can uncomment out the `IntegrationTest1.cs` file in the test project to explore the sample test.
Expand Down Expand Up @@ -216,6 +238,39 @@ The following example test project was created as part of the **Blazor & Minimal
</Project>
```

</Pivot>
<Pivot id="tunit">

```xml title="TUnit.Tests.csproj"
<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="Aspire.Hosting.Testing" Version="13.1.0" />
<PackageReference Include="TUnit" Version="*" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\AspireApp.AppHost\AspireApp.AppHost.csproj" />
</ItemGroup>

<ItemGroup>
<Using Include="System.Net" />
<Using Include="Microsoft.Extensions.DependencyInjection" />
<Using Include="Aspire.Hosting.ApplicationModel" />
<Using Include="Aspire.Hosting.Testing" />
</ItemGroup>

</Project>
```

</Pivot>

The preceding project file is fairly standard. There's a `PackageReference` to the [📦 Aspire.Hosting.Testing](https://www.nuget.org/packages/Aspire.Hosting.Testing) NuGet package, which includes the required types to write tests for Aspire projects.
Expand Down Expand Up @@ -378,6 +433,56 @@ public class IntegrationTest1
}
```

</Pivot>
<Pivot id="tunit">

```csharp title="IntegrationTest1.cs"
using Microsoft.Extensions.Logging;

namespace TUnit.Tests;

public class IntegrationTest1
{
private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(30);

[Test]
public async Task GetWebResourceRootReturnsOkStatusCode()
{
// Arrange
using var cts = new CancellationTokenSource(DefaultTimeout);
var cancellationToken = cts.Token;
var appHost = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.AspireApp_AppHost>();
appHost.Services.AddLogging(logging =>
{
logging.SetMinimumLevel(LogLevel.Debug);
// Override the logging filters from the app's configuration
logging.AddFilter(appHost.Environment.ApplicationName, LogLevel.Debug);
logging.AddFilter("Aspire.", LogLevel.Debug);
});
appHost.Services.ConfigureHttpClientDefaults(clientBuilder =>
{
clientBuilder.AddStandardResilienceHandler();
});

await using var app = await appHost.BuildAsync(cancellationToken)
.WaitAsync(DefaultTimeout, cancellationToken);
await app.StartAsync(cancellationToken)
.WaitAsync(DefaultTimeout, cancellationToken);

// Act
using var httpClient = app.CreateHttpClient("webfrontend");
await app.ResourceNotifications.WaitForResourceHealthyAsync(
"webfrontend", cancellationToken)
.WaitAsync(DefaultTimeout, cancellationToken);
using var response = await httpClient.GetAsync("/", cancellationToken);

// Assert
await Assert.That(response.StatusCode).IsEqualTo(HttpStatusCode.OK);
}
}
```

</Pivot>

The preceding code:
Expand Down Expand Up @@ -495,6 +600,36 @@ public class EnvVarTests
}
```

</Pivot>
<Pivot id="tunit">

```csharp title="EnvVarTests.cs"
using Aspire.Hosting;

namespace Tests;

public class EnvVarTests
{
[Test]
public async Task WebResourceEnvVarsResolveToApiService()
{
// Arrange
var builder = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.AspireApp_AppHost>();

var frontend = builder.CreateResourceBuilder<ProjectResource>("webfrontend");

// Act
var envVars = await frontend.Resource.GetEnvironmentVariableValuesAsync(
DistributedApplicationOperation.Publish);

// Assert
await Assert.That(envVars)
.Contains(new KeyValuePair<string, string>("APISERVICE_HTTPS", "{apiservice.bindings.https.url}"));
}
}
```

</Pivot>

The preceding code:
Expand Down Expand Up @@ -660,6 +795,55 @@ public class LoggingTest
}
```

</Pivot>
<Pivot id="tunit">

```csharp title="LoggingTest.cs"
using Microsoft.Extensions.Logging;

namespace Tests;

public class LoggingTest
{
[Test]
public async Task GetWebResourceRootReturnsOkStatusCodeWithLogging()
{
// Arrange
var builder = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.AspireApp_AppHost>();

builder.Services.ConfigureHttpClientDefaults(clientBuilder =>
{
clientBuilder.AddStandardResilienceHandler();
});

// Configure logging to capture test execution logs
builder.Services.AddLogging(logging => logging
.AddConsole() // Outputs logs to console
.AddFilter("Default", LogLevel.Information)
.AddFilter("Microsoft.AspNetCore", LogLevel.Warning)
.AddFilter("Aspire.Hosting.Dcp", LogLevel.Warning));

await using var app = await builder.BuildAsync();

await app.StartAsync();

// Act
var httpClient = app.CreateHttpClient("webfrontend");

using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
await app.ResourceNotifications.WaitForResourceHealthyAsync(
"webfrontend",
cts.Token);

var response = await httpClient.GetAsync("/");

// Assert
await Assert.That(response.StatusCode).IsEqualTo(HttpStatusCode.OK);
}
}
```

</Pivot>

### Configure log filters
Expand Down Expand Up @@ -711,6 +895,15 @@ For NUnit, consider using one of these logging packages:
- [📦 Serilog.Extensions.Logging.File](https://www.nuget.org/packages/Serilog.Extensions.Logging.File) - Writes logs to files.
- [📦 Microsoft.Extensions.Logging.Console](https://www.nuget.org/packages/Microsoft.Extensions.Logging.Console) - Outputs logs to console.

</Pivot>
<Pivot id="tunit">

For TUnit, consider using one of these logging packages:

- [📦 Dameng.Logging.TUnit](https://www.nuget.org/packages/Dameng.Logging.TUnit) - Provides an `ILogger` implementation seamlessly integrated with TUnit's `TestContext.Current.OutputWriter`.
- [📦 Serilog.Extensions.Logging.File](https://www.nuget.org/packages/Serilog.Extensions.Logging.File) - Writes logs to files.
- [📦 Microsoft.Extensions.Logging.Console](https://www.nuget.org/packages/Microsoft.Extensions.Logging.Console) - Outputs logs to console.

</Pivot>

## Summary
Expand Down