[英]Run single test against multiple configurations in Visual Studio
我們使用xUnit
和Microsoft.AspNetCore.TestHost.TestServer
設置了我們的集成測試,以針對在 ASP.NET Core 2.2 上運行的 Web API 運行測試。
我們的 Web API 是一個單一的代碼庫,可以根據一些配置或應用程序設置差異(如國家、貨幣等)單獨部署多次。
下圖試圖解釋我們的部署設置:
我們希望確保我們的集成測試針對所有部署運行。
對於這兩種部署, X和X` API 端點、請求和響應完全相同。 因此,當涉及到每個部署的集成測試時,我們希望避免重復自己。
以下是解釋我們當前測試設置的示例代碼:
TestStartup.cs
public class TestStartup : IStartup
{
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false)
.AddEnvironmentVariables()
.Build();
services.AddMvc()
.SetCompatibilityVersion(version: CompatibilityVersion.Version_2_2);
// Code to add required services based on configuration
return services.BuildServiceProvider();
}
public void Configure(IApplicationBuilder app)
{
app.UseMvc();
// Code to configure test Startup
}
}
TestServerFixture.cs
public class TestServerFixture
{
public TestServerFixture()
{
var builder = new WebHostBuilder().ConfigureServices(services =>
{
services.AddSingleton<IStartup>(new TestStartup());
});
var server = new TestServer(builder);
Client = server.CreateClient();
}
public HttpClient Client { get; private set; }
}
MyTest.cs
public class MyTest : IClassFixture<TestServerFixture>
{
private readonly TestServerFixture _fixture;
public MyTest(TestServerFixture fixture)
{
_fixture = fixture;
}
[Fact]
public void ItShouldExecuteTwice_AgainstTwoSeparateConfigurations()
{
//...
}
}
現在,我希望在MyTest
類中針對兩個不同的配置/應用程序設置或換句話說針對 Visual Studio 中的兩個不同測試部署多次運行ItShouldExecuteTwice_AgainstTwoSeparateConfigurations
測試。
我知道,我應該能夠使用構建配置(如DEBUG_SETTING1
、 DEBUG_SETTING2
)和預處理器指令( #if DEBUG_SETTING1
)的組合來實現這一點。
另一種選擇可能是擁有一個帶有通用方法的基本測試幫助程序項目和每個部署的單獨集成項目。
有沒有更好,更優雅的方法來實現這一目標?
重構測試啟動以允許根據測試需要對其進行修改
例如
public class TestStartup : IStartup {
private readonly string settings;
public TestStartup(string settings) {
this.settings = settings;
}
public void ConfigureServices(IServiceCollection services) {
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(settings, false) //<--just an example
.AddEnvironmentVariables()
.Build();
services.AddMvc()
.SetCompatibilityVersion(version: CompatibilityVersion.Version_2_2);
//...Code to add required services based on configuration
}
public void Configure(IApplicationBuilder app) {
app.UseMvc();
//...Code to configure test Startup
}
}
並通過夾具過濾該圖案
public class TestServerFixture {
static readonly Dictionary<string, TestServer> cache =
new Dictionary<string, TestServer>();
public TestServerFixture() {
//...
}
public HttpClient GetClient(string settings) {
TestServer server = null;
if(!cache.TryGetValue(settings, out server)) {
var startup = new TestStartup(settings); //<---
var builder = new WebHostBuilder()
.ConfigureServices(services => {
services.AddSingleton<IStartup>(startup);
});
server = new TestServer(builder);
cache.Add(settings, server);
}
return server.CreateClient();
}
}
最終測試本身
public class MyTest : IClassFixture<TestServerFixture> {
private readonly TestServerFixture fixture;
public MyTest(TestServerFixture fixture) {
this.fixture = fixture;
}
[Theory]
[InlineData("settings1.json")]
[InlineData("settings2.json")]
public async Task Should_Execute_Using_Configurations(string settings) {
var client = fixture.CreateClient(settings);
//...use client
}
}
@Nkosi 的帖子非常適合我們的場景和我提出的問題。 這是一種簡單、干凈且易於理解的方法,具有最大的可重用性。 答案滿分。
但是,有幾個原因導致我無法繼續采用該方法:
在建議的方法中,我們不能僅針對一種特定setting
運行測試。 這對我們很重要,因為在未來,可能有兩個不同的團隊維護他們的具體實施和部署。 使用Theory
,為所有測試僅運行一種setting
會變得有些困難。
對於每個設置/部署,我們很可能需要兩個單獨的構建和部署管道。
雖然 API 端點、 Request
和Response
今天完全相同,但我們不知道隨着我們的開發進行,它是否會繼續如此。
由於上述原因,我們還考慮了以下兩種方法:
方法一
有一個共同的class
具有公共庫Fixture
和Tests
為abstract
類
TestStartup.cs
public abstract class TestStartup : IStartup
{
public abstract IServiceProvider ConfigureServices(IServiceCollection services);
public void Configure(IApplicationBuilder app)
{
app.UseMvc();
// Code to configure test Startup
}
}
TestServerFixture.cs
public abstract class TestServerFixture
{
protected TestServerFixture(IStartup startup)
{
var builder = new WebHostBuilder().ConfigureServices(services =>
{
services.AddSingleton<IStartup>(startup);
});
var server = new TestServer(builder);
Client = server.CreateClient();
}
public HttpClient Client { get; private set; }
}
MyTest.cs
public abstract class MyTest
{
private readonly TestServerFixture _fixture;
protected MyTest(TestServerFixture fixture)
{
_fixture = fixture;
}
[Fact]
public void ItShouldExecuteTwice_AgainstTwoSeparateConfigurations()
{
//...
}
}
TestStartup.cs
public class TestStartup : Common.IntegrationTests.TestStartup
{
public override IServiceProvider ConfigureServices(IServiceCollection services)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false) // appsettings for Setting1
.AddEnvironmentVariables()
.Build();
services.AddMvc()
.SetCompatibilityVersion(version: CompatibilityVersion.Version_2_2);
// Code to add required services based on configuration
return services.BuildServiceProvider();
}
}
TestServerFixture.cs
public class TestServerFixture : Fixtures.TestServerFixture
{
public TestServerFixture() : base(new TestStartup())
{
}
}
MyTests.cs
public class MyTests : Common.IntegrationTests.MyTests, IClassFixture<TestServerFixture>
{
public MyTests(TestServerFixture fixture) : base(fixture)
{
}
}
與Setting1.IntegrationTests類似的結構
這種方法在可重用性和靈活性之間提供了良好的平衡,可以獨立運行/修改測試。 然而,我仍然不是 100% 相信這種方法,因為它意味着對於每個常見的Test
類,我們需要有一個實現,除了調用base
constructor
之外,我們什么都不做。
方法二
在第二種方法中,我們進一步采用方法 1 並嘗試解決方法 1 與共享項目的問題。 從文檔:
共享項目使您可以編寫由許多不同應用程序項目引用的公共代碼。 代碼作為每個引用項目的一部分進行編譯,並且可以包含編譯器指令以幫助將特定於平台的功能合並到共享代碼庫中。
共享項目為我們提供了兩全其美的優點,而沒有link
文件的丑陋和不必要的類inheritance
或abstraction
。 我們的新設置如下:
編輯:我寫了一篇關於此的博客文章,其中詳細討論了我們的用例和解決方案。 鏈接在這里:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.