Skip to content

Commit

Permalink
[MVC] Adds IAsyncDisposable support to WebApplicationFactory
Browse files Browse the repository at this point in the history
  • Loading branch information
HEskandari committed Jun 9, 2021
1 parent c9ed7e6 commit 5ccc740
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 12 deletions.
1 change: 1 addition & 0 deletions src/Mvc/Mvc.Testing/src/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateClient
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateClient(Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions! options) -> System.Net.Http.HttpClient!
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateDefaultClient(System.Uri! baseAddress, params System.Net.Http.DelegatingHandler![]! handlers) -> System.Net.Http.HttpClient!
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateDefaultClient(params System.Net.Http.DelegatingHandler![]! handlers) -> System.Net.Http.HttpClient!
virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.DisposeAsync() -> System.Threading.Tasks.ValueTask
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.Factories.get -> System.Collections.Generic.IReadOnlyList<Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint!>!>!
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.Server.get -> Microsoft.AspNetCore.TestHost.TestServer!
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.WithWebHostBuilder(System.Action<Microsoft.AspNetCore.Hosting.IWebHostBuilder!>! configuration) -> Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint!>!
Expand Down
54 changes: 43 additions & 11 deletions src/Mvc/Mvc.Testing/src/WebApplicationFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Net.Http;
using System.Reflection;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.TestHost;
Expand All @@ -23,9 +24,10 @@ namespace Microsoft.AspNetCore.Mvc.Testing
/// </summary>
/// <typeparam name="TEntryPoint">A type in the entry point assembly of the application.
/// Typically the Startup or Program classes can be used.</typeparam>
public class WebApplicationFactory<TEntryPoint> : IDisposable where TEntryPoint : class
public class WebApplicationFactory<TEntryPoint> : IDisposable, IAsyncDisposable where TEntryPoint : class
{
private bool _disposed;
private bool _disposedAsync;
private TestServer? _server;
private IHost? _host;
private Action<IWebHostBuilder> _configuration;
Expand Down Expand Up @@ -493,7 +495,6 @@ public HttpClient CreateDefaultClient(Uri baseAddress, params DelegatingHandler[
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

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

if (disposing)
{
foreach (var client in _clients)
if (!_disposedAsync)
{
client.Dispose();
DisposeAsync()
.AsTask()
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();
}
}

foreach (var factory in _derivedFactories)
{
factory.Dispose();
}
_disposed = true;
}

/// <inheritdoc />
public virtual async ValueTask DisposeAsync()
{
if (_disposed)
{
return;
}

if (_disposedAsync)
{
return;
}

_server?.Dispose();
_host?.StopAsync().Wait();
foreach (var client in _clients)
{
client.Dispose();
}

foreach (var factory in _derivedFactories)
{
await ((IAsyncDisposable)factory).DisposeAsync().ConfigureAwait(false);
}

_server?.Dispose();

if (_host != null)
{
await _host.StopAsync().ConfigureAwait(false);
_host?.Dispose();
}

_disposed = true;
_disposedAsync = true;

Dispose(disposing: true);
}

private class DelegatedWebApplicationFactory : WebApplicationFactory<TEntryPoint>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Xunit;

namespace Microsoft.AspNetCore.Mvc.FunctionalTests
Expand Down Expand Up @@ -80,6 +83,48 @@ public void TestingInfrastructure_GenericHost_HostShouldStopBeforeDispose()
Assert.True(callbackCalled);
}

[Fact]
public async Task TestingInfrastructure_GenericHost_HostDisposeAsync()
{
// Arrange
using var factory = new CustomizedFactory<GenericHostWebSite.Startup>().WithWebHostBuilder(ConfigureWebHostBuilder);
var sink = factory.Services.GetRequiredService<DisposableService>();

// Act
await factory.DisposeAsync();

// Assert
Assert.True(sink._asyncDisposed);
}

[Fact]
public void TestingInfrastructure_GenericHost_HostDispose()
{
// Arrange
using var factory = new CustomizedFactory<GenericHostWebSite.Startup>().WithWebHostBuilder(ConfigureWebHostBuilder);
var sink = factory.Services.GetRequiredService<DisposableService>();

// Act
factory.Dispose();

// Assert
Assert.True(sink._asyncDisposed);
}

private static void ConfigureWebHostBuilder(IWebHostBuilder builder) =>
builder.UseStartup<GenericHostWebSite.Startup>()
.ConfigureServices(s => s.AddScoped<DisposableService>());

private class DisposableService : IAsyncDisposable
{
public bool _asyncDisposed = false;
public ValueTask DisposeAsync()
{
_asyncDisposed = true;
return ValueTask.CompletedTask;
}
}

private class CustomizedFactory<TEntryPoint> : WebApplicationFactory<TEntryPoint> where TEntryPoint : class
{
public bool GetTestAssembliesCalled { get; private set; }
Expand All @@ -88,7 +133,6 @@ private class CustomizedFactory<TEntryPoint> : WebApplicationFactory<TEntryPoint
public bool CreateServerCalled { get; private set; }
public bool CreateHostCalled { get; private set; }
public IList<string> ConfigureWebHostCalled { get; private set; } = new List<string>();
public bool DisposeHostCalled { get; private set; }

protected override void ConfigureWebHost(IWebHostBuilder builder)
{
Expand Down

0 comments on commit 5ccc740

Please sign in to comment.