Reputation: 3012
I am learning ASP.NET Core and have subclassed WebApplicationFactory class with the ConfigureWebHost method overriden as shown in the listing below. I have also created a small GitHub repository to highlight the issues that I am encountering.
/// <summary>
/// Set the content root to path relative to sln, e.g. Src/WebApp
/// Configure the webhost using appsettings.json from the path where the assembly is located for
/// the startup class. The Autofac container is configured here also.
/// </summary>
/// <seealso cref="Microsoft.AspNetCore.Mvc.Testing.ConfigureWebHost(IWebHostBuilder)"/></seealso>
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
string path = Assembly.GetAssembly(typeof(WebApiTestFactory<TStartupClass>)).Location;
builder.UseSolutionRelativeContentRoot(_contentRoot)
.ConfigureAppConfiguration(cb =>
{
cb.AddJsonFile($"{Path.GetDirectoryName(path)}/appsettings.json", optional: false)
.AddEnvironmentVariables();
})
.ConfigureServices(services => services.AddAutofac());
base.ConfigureWebHost(builder);
}
This uses UseSolutionRelativeContentRoot
to set the path for content root that is relative to the directory containing *.sln files.
My integration test runs successfully within local development environment.
However, when I run the test within a docker container an InvalidOperationException
is thrown: Solution root could not be located using application root
Any ideas how I can solve this?
Exception
A total of 1 test files matched the specified pattern.
/Tests/FunctionalTests/WebApp.FunctionalTests/bin/Debug/netcoreapp3.1/WebApp.FunctionalTests.dll
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.4.1 (64-bit .NET Core 3.1.9)
[xUnit.net 00:00:00.88] Discovering: WebApp.FunctionalTests
[xUnit.net 00:00:00.94] Discovered: WebApp.FunctionalTests
[xUnit.net 00:00:00.95] Starting: WebApp.FunctionalTests
[xUnit.net 00:00:01.17] WebApp.FunctionalTests.ApiTest.WebApp_ApiController_DownloadImage [FAIL]
[xUnit.net 00:00:01.17] System.InvalidOperationException : Solution root could not be located using application root /Tests/FunctionalTests/WebApp.FunctionalTests/bin/Debug/netcoreapp3.1/.
[xUnit.net 00:00:01.18] Stack Trace:
[xUnit.net 00:00:01.18] at Microsoft.AspNetCore.TestHost.WebHostBuilderExtensions.UseSolutionRelativeContentRoot(IWebHostBuilder builder, String solutionRelativePath, String applicationBasePath, String solutionName)
[xUnit.net 00:00:01.18] at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.SetContentRoot(IWebHostBuilder builder)
[xUnit.net 00:00:01.18] at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.EnsureServer()
[xUnit.net 00:00:01.18] at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateDefaultClient(DelegatingHandler[] handlers)
[xUnit.net 00:00:01.18] at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateDefaultClient(Uri baseAddress, DelegatingHandler[] handlers)
[xUnit.net 00:00:01.18] at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateClient(WebApplicationFactoryClientOptions options)
[xUnit.net 00:00:01.18] at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateClient()
[xUnit.net 00:00:01.18] /Tests/FunctionalTests/WebApp.FunctionalTests/ApiTest.cs(45,0): at WebApp.FunctionalTests.ApiTest.WebApp_ApiController_DownloadImage()
[xUnit.net 00:00:01.18] --- End of stack trace from previous location where exception was thrown ---
[xUnit.net 00:00:01.19] Finished: WebApp.FunctionalTests
X WebApp.FunctionalTests.ApiTest.WebApp_ApiController_DownloadImage [93ms]
Error Message:
System.InvalidOperationException : Solution root could not be located using application root /Tests/FunctionalTests/WebApp.FunctionalTests/bin/Debug/netcoreapp3.1/.
Stack Trace:
at Microsoft.AspNetCore.TestHost.WebHostBuilderExtensions.UseSolutionRelativeContentRoot(IWebHostBuilder builder, String solutionRelativePath, String applicationBasePath, String solutionName)
at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.SetContentRoot(IWebHostBuilder builder)
at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.EnsureServer()
at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateDefaultClient(DelegatingHandler[] handlers)
at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateDefaultClient(Uri baseAddress, DelegatingHandler[] handlers)
at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateClient(WebApplicationFactoryClientOptions options)
at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateClient()
at WebApp.FunctionalTests.ApiTest.WebApp_ApiController_DownloadImage() in /Tests/FunctionalTests/WebApp.FunctionalTests/ApiTest.cs:line 45
--- End of stack trace from previous location where exception was thrown ---
Test Run Failed.
Total tests: 1
Failed: 1
Total time: 2.0426 Seconds
/usr/share/dotnet/sdk/3.1.403/Microsoft.TestPlatform.targets(32,5): error MSB4181: The "Microsoft.TestPlatform.Build.Tasks.VSTestTask" task returned false but did not log an error. [/Tests/FunctionalTests/WebApp.FunctionalTests/WebApp.FunctionalTests.csproj]
Test Project
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<Content Update="xunit.runner.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="coverlet.collector" Version="1.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Src\WebApp\WebApp.csproj" />
<ProjectReference Include="..\..\Functional\Utilities\WebApp.Functional.Utilities.csproj" />
<ProjectReference Include="..\..\..\Src\WebApp.S3.Contracts\WebApp.S3.Contracts.csproj" />
</ItemGroup>
</Project>
Also added Microsoft.AspNetCore.Mvc.Testing dependency to the test project as detailed here and still get the same result:
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.9" />
Also tried adding WebApplicationFactoryContentRoot attribute in Test Project:
using System;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Autofac.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;
using Xunit.Abstractions;
using WebApp.S3.Contracts;
using WebApp.Functional.Utilities;
/// key should match FullName assembly attributed of TStartup : WebApplicationFactory<TStartup>
/// https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.testing.webapplicationfactorycontentrootattribute?view=aspnetcore-3.0
[assembly: WebApplicationFactoryContentRoot(
key: "WebApp.FunctionalTests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
contentRootPath: "../../../../../../Src/WebApp",
contentRootTest: "Program.cs",
priority: "1")]
namespace WebApp.FunctionalTests
{
public class ApiTest
{
private TestingBase<WebAppTestStartup> _testing;
private string _settingsFile;
private string GetSettingsFile()
{
const string AspNetCoreEnvironment="ASPNETCORE_ENVIRONMENT";
const string DefaultSettingsFile = "appsettings.Local.json";
var envVar = System.Environment.GetEnvironmentVariable(AspNetCoreEnvironment);
if(envVar != null)
return $"appsettings.{envVar}.json";
else
return DefaultSettingsFile;
}
/// <summary>
/// Create Web Application Factory with content root at Src/WebApp using
/// appsettings file derived from ASPNETCORE_ENVIRONMENT variable. If
/// the variable is unset then defaults to appsettings.Local.json
/// </summary>
/// <param name="output">xUnit output stream</param>
public ApiTest(ITestOutputHelper output)
{
const string contentRoot = "Src/WebApp";
_settingsFile = GetSettingsFile();
string startupPath = Assembly.GetAssembly(typeof(WebApiTestFactory<WebAppTestStartup>)).Location;
string settingsFilePath = $"{Path.GetDirectoryName(startupPath)}/{_settingsFile}";
_testing = new TestingBase<WebAppTestStartup>(output, contentRoot, settingsFilePath);
}
[Fact]
public async Task WebApp_ApiController_DownloadImage()
{
const string TestData = "test";
const string ObjectName = "objectName";
byte[] Payload = Encoding.UTF8.GetBytes(TestData);
// upload a sample image
using (var client = _testing.Factory.CreateClient())
using (var scope = _testing.Factory.Server.Host.Services.CreateScope())
using (var byteStream = new MemoryStream(Payload))
using (var stream = new BufferedStream(byteStream))
{
var s3Client = scope.ServiceProvider.GetRequiredService<IS3Service>();
await s3Client.UploadAsync(stream, ObjectName);
var response = await client.GetAsync(ApiRoutes.Get.Image(ObjectName));
response.EnsureSuccessStatusCode();
string result = await response.Content.ReadAsStringAsync();
Assert.Equal(TestData, result);
}
}
}
}
Also tried adding this to my csproj file:
<Target Name="AddGitMetadaAssemblyAttributes" BeforeTargets="CoreGenerateAssemblyInfo">
<ItemGroup>
<AssemblyAttribute Include="Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryContentRoot">
<_Parameter1>WebApp.FunctionalTests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</_Parameter1>
<_Parameter2>../../../Src/WebApp</_Parameter2>
<_Parameter3>appsettings.Development.json</_Parameter3>
<_Parameter4>1</_Parameter4>
</AssemblyAttribute>
</ItemGroup>
</Target>
WebApp.FunctionalTests
is the assembly that contains my Startup class. This is derived from the live Startup
class that exists in a different assembly.
Resorted to creating a smaller project and debug from there when have time. Also raised an issue on Github.
Upvotes: 3
Views: 1752
Reputation: 1715
Microsoft.AspNetCore.Mvc.Testing
already has a class for creating TestsServers for integration tests with .net core and ways to replace configuration, not sure why you need to create your own. I would recommend adding your configuration to .net core´s DI and replacing your configuration in ConfigureServices
.
I´ve written integration tests for a WebAPI like this. You can enclose this for your specific needs into an abstract class that you inherit from in each of your test classes.
var appFactory = new WebApplicationFactory<Startup>()
.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services => {
// Remove/Add Services from .net CORE DI
services.Remove(services.SingleOrDefault(
d => d.ServiceType == typeof(IExampleDbSettings)));
services.AddSingleton<IExampleDbSettings>(new TestDbSettings { ... });
}
}
// Test HTTP Client
var httpClient = appFactory.CreateClient(); //Http Client to connect to TestServer
// Request a Token and add it to client if needed
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", "<jwt_token>");
Upvotes: 1