-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Support the new top level statements with WebApplicationFactory #33462
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
a999758
Support the new top level statements with WebApplicationFactory
davidfowl bf55341
Some code clean up and added more comments
davidfowl ea0beb1
Change fallback order
davidfowl 593c949
Added WebApplicationFactory test for top level statements
davidfowl 8e1bd4d
Allow CreateWebHostBuilder to return nullable builder
captainsafia 677b706
Merge branch 'main' into davidfowl/webapplicationfactory
captainsafia File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Configuration; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Hosting; | ||
|
||
namespace Microsoft.AspNetCore.Mvc.Testing | ||
{ | ||
// This host builder captures calls to the IHostBuilder then replays them in the call to ConfigureHostBuilder | ||
internal class DeferredHostBuilder : IHostBuilder | ||
{ | ||
public IDictionary<object, object> Properties { get; } = new Dictionary<object, object>(); | ||
|
||
private Action<IHostBuilder> _configure; | ||
private Func<string[], object>? _hostFactory; | ||
|
||
public DeferredHostBuilder() | ||
{ | ||
_configure = b => | ||
{ | ||
// Copy the properties from this builder into the builder | ||
// that we're going to receive | ||
foreach (var pair in Properties) | ||
{ | ||
b.Properties[pair.Key] = pair.Value; | ||
} | ||
}; | ||
} | ||
|
||
public IHost Build() | ||
{ | ||
// This will never be null if the case where Build is being called | ||
var host = (IHost)_hostFactory!(Array.Empty<string>()); | ||
|
||
// We can't return the host directly since we need to defer the call to StartAsync | ||
return new DeferredHost(host); | ||
} | ||
|
||
public IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate) | ||
{ | ||
_configure += b => b.ConfigureAppConfiguration(configureDelegate); | ||
return this; | ||
} | ||
|
||
public IHostBuilder ConfigureContainer<TContainerBuilder>(Action<HostBuilderContext, TContainerBuilder> configureDelegate) | ||
{ | ||
_configure += b => b.ConfigureContainer(configureDelegate); | ||
return this; | ||
} | ||
|
||
public IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate) | ||
{ | ||
_configure += b => b.ConfigureHostConfiguration(configureDelegate); | ||
return this; | ||
} | ||
|
||
public IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate) | ||
{ | ||
_configure += b => b.ConfigureServices(configureDelegate); | ||
return this; | ||
} | ||
|
||
public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory) where TContainerBuilder : notnull | ||
{ | ||
_configure += b => b.UseServiceProviderFactory(factory); | ||
return this; | ||
} | ||
|
||
public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory) where TContainerBuilder : notnull | ||
{ | ||
_configure += b => b.UseServiceProviderFactory(factory); | ||
return this; | ||
} | ||
|
||
public void ConfigureHostBuilder(object hostBuilder) | ||
{ | ||
_configure(((IHostBuilder)hostBuilder)); | ||
} | ||
|
||
public void SetHostFactory(Func<string[], object> hostFactory) | ||
{ | ||
_hostFactory = hostFactory; | ||
} | ||
|
||
private class DeferredHost : IHost, IAsyncDisposable | ||
{ | ||
private readonly IHost _host; | ||
|
||
public DeferredHost(IHost host) | ||
{ | ||
_host = host; | ||
} | ||
|
||
public IServiceProvider Services => _host.Services; | ||
|
||
public void Dispose() => _host.Dispose(); | ||
|
||
public ValueTask DisposeAsync() | ||
{ | ||
if (_host is IAsyncDisposable disposable) | ||
{ | ||
return disposable.DisposeAsync(); | ||
} | ||
Dispose(); | ||
return default; | ||
} | ||
|
||
public Task StartAsync(CancellationToken cancellationToken = default) | ||
{ | ||
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); | ||
|
||
// Wait on the existing host to start running and have this call wait on that. This avoids starting the actual host too early and | ||
// leaves the application in charge of calling start. | ||
|
||
using var reg = cancellationToken.UnsafeRegister(_ => tcs.TrySetCanceled(), null); | ||
|
||
// REVIEW: This will deadlock if the application creates the host but never calls start. This is mitigated by the cancellationToken | ||
// but it's rarely a valid token for Start | ||
_host.Services.GetRequiredService<IHostApplicationLifetime>().ApplicationStarted.UnsafeRegister(_ => tcs.TrySetResult(), null); | ||
|
||
return tcs.Task; | ||
} | ||
|
||
public Task StopAsync(CancellationToken cancellationToken = default) => _host.StopAsync(cancellationToken); | ||
} | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
src/Mvc/test/Mvc.FunctionalTests/SimpleWithWebApplicationBuilderTests.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// 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.Net; | ||
using System.Net.Http; | ||
using System.Threading.Tasks; | ||
using Xunit; | ||
|
||
namespace Microsoft.AspNetCore.Mvc.FunctionalTests | ||
{ | ||
public class SimpleWithWebApplicationBuilderTests : IClassFixture<MvcTestFixture<SimpleWebSiteWithWebApplicationBuilder.FakeStartup>> | ||
{ | ||
public SimpleWithWebApplicationBuilderTests(MvcTestFixture<SimpleWebSiteWithWebApplicationBuilder.FakeStartup> fixture) | ||
{ | ||
Client = fixture.CreateDefaultClient(); | ||
} | ||
|
||
public HttpClient Client { get; } | ||
|
||
[Fact] | ||
public async Task HelloWorld() | ||
{ | ||
// Arrange | ||
var expected = "Hello World"; | ||
|
||
// Act | ||
var content = await Client.GetStringAsync("http://localhost/"); | ||
|
||
// Assert | ||
Assert.Equal(expected, content); | ||
} | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
src/Mvc/test/WebSites/SimpleWebSiteWithWebApplicationBuilder/FakeEntryPoint.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
|
||
/// <summary> | ||
/// This is a class we use to reference this assembly statically from tests | ||
/// </summary> | ||
namespace SimpleWebSiteWithWebApplicationBuilder | ||
{ | ||
public class FakeStartup | ||
davidfowl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not do:
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
b.Properties is read only.