这是我遇到的一个很奇怪的问题。我正在尝试设置TestServer
来测试ASP.NET API。在此特定测试中,我想使用不同的配置appsettings.json
而不是项目的appsettings.custom.json
。因此,当我创建自己的TestServer
时,这就是我要做的:
protected TestServer CreateTestServer()
{
var testConfiguration = new AspNetTestConfiguration(); // configures middleware, authentication, default/static files
var configFile = "appsettings.custom.json";
var config = new ConfigurationBuilder().AddJsonFile(configFile).Build();
Container.Configure(cfg => cfg.For<IConfiguration>().Use(config)); // attempt 1 - that's my StructureMap container used for DI
// Create the WebHostBuilder used by the test server
var webHostBuilder = WebHost.CreateDefaultBuilder()
.ConfigureServices(svc =>
{
svc.AddSingleton(Container);
svc.AddSingleton(config); // attempt 2
svc.AddSingleton(testConfiguration);
})
.UseConfiguration(config) // attempt 3
.UseStartup<TestStartup>();
return new TestServer(webHostBuilder);
}
但是,当我进入控制器时,该控制器通过依赖项注入在构造函数中提供了IConfiguration
,我访问IConfiguration
中的设置,发现值是{ {1}}!
正如您在上面看到的,我尝试了3种不同的方法来说服appsettings.json
必须使用TestServer
中的配置值,但无济于事。
为什么它仍然坚持使用appsettings.custom.json
,我该如何解决?
更新:
越来越好奇。我尝试将appsettings.json
重命名为appsettings.json
并将其设置为默认设置文件。现在,神奇的是,我的appsettings.default.json
在我期望的地方使用TestServer
!
答案 0 :(得分:0)
确保您设置了要更新的测试json文件,如果更新则将其复制到输出目录
在.NET Core 2.x中,我们将这种情况用于这种情况
public class TestServerFixture : IDisposable
{
private readonly TestServer _testServer;
public HttpClient Client { get; }
public TestServerFixture(string assemblyName)
{
IWebHostBuilder builder = new WebHostBuilder()
.UseContentRoot(Path.Combine(ConfigHelper.GetSolutionBasePath(), assemblyName))
.UseEnvironment("Development")
.UseConfiguration(new ConfigurationBuilder()
.AddJsonFile("appsettings.tests.integration.json") //the file is set to be copied to the output directory if newer
.Build()
).UseStartup(assemblyName);
_testServer = new TestServer(builder);
Client = _testServer.CreateClient();
}
public void Dispose()
{
Client.Dispose();
_testServer.Dispose();
}
}
并获取解决方案的基本路径
/// <summary>
/// Gets the current soution base path
/// </summary>
/// <returns></returns>
public static string GetSolutionBasePath()
{
var appPath = PlatformServices.Default.Application.ApplicationBasePath;
var binPosition = appPath.IndexOf("\\bin", StringComparison.Ordinal);
var basePath = appPath.Remove(binPosition);
var backslashPosition = basePath.LastIndexOf("\\", StringComparison.Ordinal);
basePath = basePath.Remove(backslashPosition);
return basePath;
}
答案 1 :(得分:0)
Daniel 的回答很棒,但我有一个更简洁的解决方案,不需要明确指定要使用或调整路径的 appsettings.json
。
需要注意的最重要的一点是 TestServer 使用的是 WebHostBuilder
,而不是泛型,因此您需要教这个 WebHostBuilder
如何从 json 文件、环境、秘密等中读取设置。否则就不能。
这对 .NET Core 3.1+(和 .NET 5)有效,我使用与公认答案类似的方法,其中我的集成测试扩展了一个抽象类,为我设置了一切。
public abstract class FunctionalTest
: GivenAsync_WhenAsync_Then_Test
{
private readonly IServiceProvider _serviceProvider;
protected HttpClient HttpClient { get; }
protected IConfiguration Configuration { get; }
protected FunctionalTest()
{
var server =
new TestServer(
new WebHostBuilder()
.UseStartup<Startup>()
.UseCommonConfiguration()
.UseEnvironment("Test")
.ConfigureTestServices(ConfigureTestServices));
HttpClient = server.CreateClient();
_serviceProvider = server.Services;
Configuration = _serviceProvider.GetService<IConfiguration>();
}
protected T GetService<T>() where T : class
{
return _serviceProvider.GetService<T>();
}
protected virtual void ConfigureTestServices(IServiceCollection services)
{
// Here replace DI container registrations for test executions,
// although you can also do this at the specific test since this is a virtual method that can be overriden
}
}
有几点需要注意:
GivenAsync_WhenAsync_Then_Test
只是一个自定义的简单抽象类,我用来强迫我使用 Given When Then 方法编写测试。与此问题无关,但如果您想查看它,请访问 https://gitlab.com/sunnyatticsoftware/sasw-test-support
我有一个名为 UseCommonConfiguration
的自定义扩展方法,它负责教 IWebHostBuilder
如何读取设置。实现是标准的
public static IWebHostBuilder UseCommonConfiguration(this IWebHostBuilder builder)
{
builder.ConfigureAppConfiguration((hostingContext, config) =>
{
var env = hostingContext.HostingEnvironment;
config
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
if (env.IsDevelopment())
{
var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
config.AddUserSecrets(appAssembly, optional: true);
}
});
return builder;
}
我还将环境设置为 "Test"
,这样我只是在处理不同的测试执行环境,其他一切都相同(与实际应用程序相同的 Startup
类,等等。)。有了这个,我告诉 AspNet 测试服务器寻找一个 appsettings.Test.json
。因此,继续使用要使用的测试值的特定 appsettings.Test.json
,并将其放置在测试项目的根目录下(如果需要),并带有 BuildAction Content
和 Copy to Output Directory Copy if newer
(与标准 appsettings.json
或 appsettings.Development.json
行 ConfigureTestServices(sp => { /* .. */})
允许替换/修改启动时发生的 DI 容器注册。用模拟或类似的东西替换一些服务非常有用。
然后,您的测试类可以扩展此抽象类 FunctionalTest
并使用 HttpClient
向测试服务器发送 http 请求,如 _result = await HttpClient.PostAsync("api/whatever", myStringContent);