长期阅读
使用简单的设置运行集成测试的优势很大。您可以在此过程中早些时候找到错误,并为您提供代码按预期工作的保险。按照我的指南进行下面的指南,以运行Azure功能和Cosmos DB的集成测试。
更多的
集成测试 - 是一种软件测试,其中将软件应用程序的不同单元,模块或组件作为合并实体测试。
如果可能的话,可以在本地旋转依赖项是一个很好的实力。您应该再次选择运行测试本地数据库,而不是远程数据库。这有几个优势:
- 测试在本地运行的速度更快,而不是针对远程数据库。
- 您独立于其他开发人员进行测试。来自其他机器的测试数据不会影响您的数据库。
测试的系统
Azure功能是按需提供的云服务,可提供运行应用程序所需的所有不断更新的基础架构和资源。您专注于对您最重要的代码,以最有生产力的语言对您来说,其余的功能都可以处理。
在此示例中,我将togther togher azure函数放置。它将端点暴露于 create 汽车:
public class CarFunction
{
private readonly ICarRepository _carRepository;
private readonly ILogger<CarFunction> _logger;
public CarFunction(ICarRepository carRepository, ILogger<CarFunction> logger)
{
_carRepository = carRepository ?? throw new ArgumentNullException(nameof(carRepository));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
[FunctionName("CreateCar")]
public async Task<IActionResult> CreateCar([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "cars")] CarRequest request, HttpRequest req)
{
try
{
if (string.IsNullOrWhiteSpace(request.Name))
return new BadRequestObjectResult("Name is mandatory.");
var createdCar = await _carService.CreateCar(request);
return new CreatedResult("/cars/" + createdCar.Id, createdCar);
}
catch (Exception ex)
{
return new ObjectResult(ex.Message) { StatusCode = 500 };
}
}
}
我们将使用Cosmos DB存储汽车。这是存储库的外观:
public class CarRepository : ICarRepository
{
private CosmosClient _cosmosClient;
private Container _container;
public CarRepository(IOptions<Configuration> configuration)
{
this._cosmosClient = new CosmosClient(configuration.Value.ConnectionString);
this._container = _cosmosClient.GetContainer("CarDatabase", "Cars");
}
public async Task<Car> Create(Car car)
{
var itemResponse = await _container.CreateItemAsync(car, new PartitionKey(car.Id));
return itemResponse.Resource;
}
}
由于我们使用函数应用程序,我们必须具有Startup.cs
:
public class Startup : FunctionsStartup
{
private const string configurationSection = "Cars:Database";
protected virtual IConfigurationRoot GetConfigurationRoot(IFunctionsHostBuilder functionsHostBuilder)
{
var local = new ConfigurationBuilder()
.AddJsonFile(Path.Combine(Environment.CurrentDirectory, "local.settings.json"), true, true)
.AddEnvironmentVariables()
.Build();
return local;
}
public override void Configure(IFunctionsHostBuilder builder)
{
var local = GetConfigurationRoot(builder);
var config = new ConfigurationBuilder().AddEnvironmentVariables();
var configurationSection = local.GetSection(configurationSection);
builder.Services.Configure<Configuration>(configurationSection);
var configuration = config.Build();
builder.Services.AddInfrastructure(configuration);
}
}
Azure Cosmos DB模拟器
我们将针对Azure CosmosDB的局部实例运行我们的间测试。可以在此处下载可用。启动后,它的外观将如何:
依赖注射
集成测试设置的残酷部分是限制依赖注入。您需要设置以下类:
testStartup
我们将从Startup
类派生到定义测试的依赖性注入。
public class TestStartup : Startup
{
protected override IConfigurationRoot GetConfigurationRoot(IFunctionsHostBuilder functionsHostBuilder)
{
var currentDirectory = AppDomain.CurrentDomain.BaseDirectory;
var configuration = new ConfigurationBuilder()
.SetBasePath(currentDirectory)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile("local.settings.json", true, true)
.AddEnvironmentVariables()
.Build();
return configuration;
}
public override void Configure(IFunctionsHostBuilder builder)
{
base.Configure(builder);
builder.Services.AddTransient<CarsFunction>();
}
}
配置
不建议将钥匙和秘密存储在GIT存储库中。对于本地开发,我们可以使用local.settings.json
存储配置。但是,我们可以使用appsettings.json
来管理配置。例如,我们可以在Azure管道中使用pipeline variables和FileTransform。 Here是我们如何实现它的示例。
local.settings.json
(不应离开您的本地机器):
{
"Cars": {
"Database": {
"ConnectionString": "AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
}
}
}
appsettings.json
(停留在源控制中,由CI/CD管道纳入)
{
"Cars": {
"Database": {
"ConnectionString": ""
}
}
}
测试初始化器
我们想使用TestStartup
使用测试主机进行集成测试。
public class TestsInitializer
{
public TestsInitializer()
{
var host = new HostBuilder()
.ConfigureWebJobs(builder => builder.UseWebJobsStartup(typeof(TestStartup), new WebJobsBuilderContext(), NullLoggerFactory.Instance))
.Build();
ServiceProvider = host.Services;
}
public IServiceProvider ServiceProvider { get; }
}
我们还需要通过从ICollectionFixture
类派生来包括收集定义。
[CollectionDefinition(Name)]
public class IntegrationTestsCollection : ICollectionFixture<TestsInitializer>
{
public const string Name = nameof(IntegrationTestsCollection);
}
集成测试
我们最终可以实现我们的integration test
:
[Collection(IntegrationTestsCollection.Name)]
public class CarFunctionTests : IClassFixture<TestStartup>, IAsyncLifetime
{
private CarFunction _carFunction;
private readonly TestsInitializer _testsInitializer;
private readonly CosmosClient _cosmosClient;
private Container _container;
private readonly string _carId;
public CarFunctionTests(TestsInitializer testsInitializer)
{
_testsInitializer = testsInitializer;
var cosmosDatabaseConfiguration = testsInitializer.ServiceProvider.GetService<IOptions<CarConfiguration>>();
_cosmosClient = new CosmosClient(cosmosDatabaseConfiguration.Value.EndpointUri, cosmosDatabaseConfiguration.Value.PrimaryKey);
_carFunction = _testsInitializer.ServiceProvider.GetService<CarFunction>();
}
[Fact]
public async void TestCreateCar()
{
// Arrange
var carName = $"BMW - {Guid.NewGuid()}";
var carRequest = new CarRequest { Name = carName };
// Act
var response = await _carFunction.CreateCar(, new DefaultHttpContext().Request);
var createdResponse = (CreatedResult)_response;
_carId = (createdResponse.Value as Car).Id;
// Assert
Assert.IsType<CreatedResult>(createdResponse);
Assert.Equal(carName, (createdResponse.Value as Car).Name);
}
public async Task InitializeAsync()
{
var databaseResponse = await _cosmosClient.CreateDatabaseIfNotExistsAsync("CarDatabase");
var database = databaseResponse.Database;
var containerResponse = await database.CreateContainerIfNotExistsAsync("Cars", "/id");
_container = containerResponse.Container;
}
public async Task DisposeAsync()
{
await _container.DeleteItemAsync<Car>(_carId, new PartitionKey(_carId));
}
}
运行测试
我们可以与dotnet test
命令进行集成测试。
概括
我们为Azure函数和Cosmos DB编写了集成测试。我们还使用了可更换的配置和配置的依赖注入为我们工作。