Skip to content

Commit 5ccc740

Browse files
authored
[MVC] Adds IAsyncDisposable support to WebApplicationFactory
1 parent c9ed7e6 commit 5ccc740

File tree

3 files changed

+89
-12
lines changed

3 files changed

+89
-12
lines changed

src/Mvc/Mvc.Testing/src/PublicAPI.Unshipped.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateClient
3232
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateClient(Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions! options) -> System.Net.Http.HttpClient!
3333
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateDefaultClient(System.Uri! baseAddress, params System.Net.Http.DelegatingHandler![]! handlers) -> System.Net.Http.HttpClient!
3434
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateDefaultClient(params System.Net.Http.DelegatingHandler![]! handlers) -> System.Net.Http.HttpClient!
35+
virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.DisposeAsync() -> System.Threading.Tasks.ValueTask
3536
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.Factories.get -> System.Collections.Generic.IReadOnlyList<Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint!>!>!
3637
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.Server.get -> Microsoft.AspNetCore.TestHost.TestServer!
3738
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.WithWebHostBuilder(System.Action<Microsoft.AspNetCore.Hosting.IWebHostBuilder!>! configuration) -> Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint!>!

src/Mvc/Mvc.Testing/src/WebApplicationFactory.cs

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Net.Http;
1010
using System.Reflection;
1111
using System.Text.Json;
12+
using System.Threading.Tasks;
1213
using Microsoft.AspNetCore.Hosting;
1314
using Microsoft.AspNetCore.Hosting.Server;
1415
using Microsoft.AspNetCore.TestHost;
@@ -23,9 +24,10 @@ namespace Microsoft.AspNetCore.Mvc.Testing
2324
/// </summary>
2425
/// <typeparam name="TEntryPoint">A type in the entry point assembly of the application.
2526
/// Typically the Startup or Program classes can be used.</typeparam>
26-
public class WebApplicationFactory<TEntryPoint> : IDisposable where TEntryPoint : class
27+
public class WebApplicationFactory<TEntryPoint> : IDisposable, IAsyncDisposable where TEntryPoint : class
2728
{
2829
private bool _disposed;
30+
private bool _disposedAsync;
2931
private TestServer? _server;
3032
private IHost? _host;
3133
private Action<IWebHostBuilder> _configuration;
@@ -493,7 +495,6 @@ public HttpClient CreateDefaultClient(Uri baseAddress, params DelegatingHandler[
493495
public void Dispose()
494496
{
495497
Dispose(true);
496-
GC.SuppressFinalize(this);
497498
}
498499

499500
/// <summary>
@@ -512,22 +513,53 @@ protected virtual void Dispose(bool disposing)
512513

513514
if (disposing)
514515
{
515-
foreach (var client in _clients)
516+
if (!_disposedAsync)
516517
{
517-
client.Dispose();
518+
DisposeAsync()
519+
.AsTask()
520+
.ConfigureAwait(false)
521+
.GetAwaiter()
522+
.GetResult();
518523
}
524+
}
519525

520-
foreach (var factory in _derivedFactories)
521-
{
522-
factory.Dispose();
523-
}
526+
_disposed = true;
527+
}
528+
529+
/// <inheritdoc />
530+
public virtual async ValueTask DisposeAsync()
531+
{
532+
if (_disposed)
533+
{
534+
return;
535+
}
536+
537+
if (_disposedAsync)
538+
{
539+
return;
540+
}
524541

525-
_server?.Dispose();
526-
_host?.StopAsync().Wait();
542+
foreach (var client in _clients)
543+
{
544+
client.Dispose();
545+
}
546+
547+
foreach (var factory in _derivedFactories)
548+
{
549+
await ((IAsyncDisposable)factory).DisposeAsync().ConfigureAwait(false);
550+
}
551+
552+
_server?.Dispose();
553+
554+
if (_host != null)
555+
{
556+
await _host.StopAsync().ConfigureAwait(false);
527557
_host?.Dispose();
528558
}
529559

530-
_disposed = true;
560+
_disposedAsync = true;
561+
562+
Dispose(disposing: true);
531563
}
532564

533565
private class DelegatedWebApplicationFactory : WebApplicationFactory<TEntryPoint>

src/Mvc/test/Mvc.FunctionalTests/TestingInfrastructureInheritanceTests.cs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System;
45
using System.Collections.Generic;
56
using System.Linq;
67
using System.Reflection;
8+
using System.Threading.Tasks;
79
using Microsoft.AspNetCore.Hosting;
810
using Microsoft.AspNetCore.Mvc.Testing;
911
using Microsoft.AspNetCore.TestHost;
1012
using Microsoft.Extensions.Configuration;
1113
using Microsoft.Extensions.Hosting;
14+
using Microsoft.Extensions.DependencyInjection;
1215
using Xunit;
1316

1417
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
@@ -80,6 +83,48 @@ public void TestingInfrastructure_GenericHost_HostShouldStopBeforeDispose()
8083
Assert.True(callbackCalled);
8184
}
8285

86+
[Fact]
87+
public async Task TestingInfrastructure_GenericHost_HostDisposeAsync()
88+
{
89+
// Arrange
90+
using var factory = new CustomizedFactory<GenericHostWebSite.Startup>().WithWebHostBuilder(ConfigureWebHostBuilder);
91+
var sink = factory.Services.GetRequiredService<DisposableService>();
92+
93+
// Act
94+
await factory.DisposeAsync();
95+
96+
// Assert
97+
Assert.True(sink._asyncDisposed);
98+
}
99+
100+
[Fact]
101+
public void TestingInfrastructure_GenericHost_HostDispose()
102+
{
103+
// Arrange
104+
using var factory = new CustomizedFactory<GenericHostWebSite.Startup>().WithWebHostBuilder(ConfigureWebHostBuilder);
105+
var sink = factory.Services.GetRequiredService<DisposableService>();
106+
107+
// Act
108+
factory.Dispose();
109+
110+
// Assert
111+
Assert.True(sink._asyncDisposed);
112+
}
113+
114+
private static void ConfigureWebHostBuilder(IWebHostBuilder builder) =>
115+
builder.UseStartup<GenericHostWebSite.Startup>()
116+
.ConfigureServices(s => s.AddScoped<DisposableService>());
117+
118+
private class DisposableService : IAsyncDisposable
119+
{
120+
public bool _asyncDisposed = false;
121+
public ValueTask DisposeAsync()
122+
{
123+
_asyncDisposed = true;
124+
return ValueTask.CompletedTask;
125+
}
126+
}
127+
83128
private class CustomizedFactory<TEntryPoint> : WebApplicationFactory<TEntryPoint> where TEntryPoint : class
84129
{
85130
public bool GetTestAssembliesCalled { get; private set; }
@@ -88,7 +133,6 @@ private class CustomizedFactory<TEntryPoint> : WebApplicationFactory<TEntryPoint
88133
public bool CreateServerCalled { get; private set; }
89134
public bool CreateHostCalled { get; private set; }
90135
public IList<string> ConfigureWebHostCalled { get; private set; } = new List<string>();
91-
public bool DisposeHostCalled { get; private set; }
92136

93137
protected override void ConfigureWebHost(IWebHostBuilder builder)
94138
{

0 commit comments

Comments
 (0)