在微服务环境中,我需要为基于契约的测试构建一个框架。我目前正在研究如何将单个服务与其外部依赖项隔离开来,以便执行 Provider 测试。
我需要做的是:
- 保持 WebApi 项目完好无损
- 使用一些配置差异启动 WepApi 的实例
- 模拟选定的依赖项
我的解决方案结构是这样的:
Case-Solution/
├── src/
| ├──Case.Api
| └──Case.Application
├── test/
| ├──Case.Api.Unittest
| ├──(other tests)
| ├──Case.Pact.CunsumerTest
| └──Case.Pact.ProviderTest
我已阅读有关 dotnet 中的 Pact Tests 的本指南。专注于Case.Pace.ProviderTest
,我需要以Case.Api
编程方式从Case.Pact.ProviderTest
(以及另一个用于 Pact it self 的 WebHost)开始并替换它的一些依赖项。
到目前为止,我得到了这个:
public class ProviderApiTests : IDisposable
{
private string ProviderUri { get; }
private string PactServiceUri { get; }
private IWebHost PactServiceWebHost { get; }
private IWebHost CasesWebHost { get; }
private ITestOutputHelper OutputHelper { get; }
public static IConfiguration CaseConfiguration { get; } = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT" ?? "Production"}.json", optional: true)
.AddEnvironmentVariables()
.Build();
public ProviderApiTests(ITestOutputHelper output)
{
OutputHelper = output;
ProviderUri = "http://localhost:9000";
PactServiceUri = "http://localhost:9001";
CasesWebHost = WebHost.CreateDefaultBuilder()
.UseUrls(ProviderUri)
.UseStartup<CaseStartup>()
.UseConfiguration(CaseConfiguration)
.Build();
CasesWebHost.Start();
PactServiceWebHost = WebHost.CreateDefaultBuilder()
.UseUrls(PactServiceUri)
.UseStartup<ProviderServerStartup>()
.Build();
PactServiceWebHost.Start();
}
[Fact]
public void EnsureProviderApiHonoursPactWithConsumer()
{
//Arrange
var config = new PactVerifierConfig
{
Outputters = new List<IOutput>
{
new XUnitOutput(OutputHelper)
},
Verbose = true,
CustomHeader = new KeyValuePair<string, string>("X-apikey", "XXX")
};
//Act //Assert
IPactVerifier pactVerifier = new PactVerifier(config);
pactVerifier.ProviderState($"{PactServiceUri}/provider-states")
.ServiceProvider("CaseProvider", ProviderUri)
.HonoursPactWith("CaseConsumer")
.PactUri(@"..\..\..\..\..\pacts\caseconsumer-caseprovider.json")
.Verify();
}
#region IDisposable Support
//IDisposable code
#endregion
}
在包含的行中,.UseStartup<CaseStartup>()
我只是简单地复制Startup.cs
并Case.Api
更改了所需的依赖项,效果很好。
但我想要一个更通用的解决方案。仅仅复制代码并称之为一天是不对的:),因为它不是通用的,也不能用于其他服务。
所以我一直在挖掘,并想出了以下内容。
从我意识到的其他程序集中添加控制器
,使用来自不同程序集的 StartUp 启动 IWebhost 不会自动从该程序集中添加控制器。这需要明确地完成。所以我这样做了:
public void ConfigureServices(IServiceCollection services)
{
var assembly = Assembly.Load("Case.Api");
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddApplicationPart(assembly)
.AddControllersAsServices();
......
}
惊人的!!!到目前为止,一切都很好。
下一期:
替换依赖:
reding这篇文章,我创建了替换依赖的扩展方法:
public static void Replace<TRegisteredType>(this IServiceCollection services, TRegisteredType replcement)
{
for (var i = 0; i < services.Count; i++)
{
if (services[i].ServiceType == typeof(TRegisteredType))
{
services[i] = new ServiceDescriptor(typeof(TRegisteredType), replcement);
}
}
}
所以我可以像这样替换我想要的依赖项:(在本例中为 QueryHandler)
public void ConfigureServices(IServiceCollection services)
{
.....
var queryHandler = Substitute.For<IQueryHandler<Query, QueryResult>>();
queryHandler.Handle(Arg.Any<Query>()).Returns(new QueryResult(...));
services.Replace(queryHandler);
......
}
但这并不能解决我复制代码的问题。
我的梦想是能够使用Startup.cs
fromCase.Api
并以某种方式调整 DI 以替换依赖项,而无需所有冗余代码。
任何输入都会受到高度评价。
谢谢 :)