我想运行单元/集成测试,这些测试使用Azure存储模拟器,而不是Azure DevOps构建中的实际存储。
模拟器作为Azure SDK的一部分安装在托管生成控制器上的正常位置(C:\Program Files(x86)\Microsoft SDKs\Azure\Storage emulator \AzureStorageEmulator.exe)。
但是,模拟器在生成控制器上处于未初始化状态。当尝试从命令行运行命令Init时,我得到以下错误:
This operation requires an interactive window station
是否有已知的解决方法或计划在Azure DevOps构建中支持模拟器?
尽管这里的答案都相反,但我已经在VS2017托管的构建代理上运行Azure Storage Emulator一年多了。
诀窍是先初始化SQLLocalDB(模拟器使用它),然后启动模拟器。您可以通过运行以下命令行任务来完成此操作:
sqllocaldb create MSSQLLocalDB
sqllocaldb start MSSQLLocalDB
sqllocaldb info MSSQLLocalDB
"C:Program Files (x86)Microsoft SDKsAzureStorage EmulatorAzureStorageEmulator.exe" start
如前所述,您无法运行Azure存储模拟器。不过,您可以运行的是Azurite,这是一个开源的替代方案。
请注意:Azurite可以模拟Blob、表和队列。然而,我只以这种方式使用了blob存储模拟。
在构建配置开始时,添加一个运行自定义nuget命令install Azurite -version 2.2.2
的nuget步骤。然后添加一个运行start /b $(Build.SourcesDirectory)Azurite.2.2.2toolsblob.exe
的命令行步骤。
它与Azure Storage Emulator在同一端口上运行,因此您可以使用标准连接字符串。
否,Hosted Build Controller不在交互模式下运行,因此模拟器无法在该环境下工作。参见Q&XAML生成的托管内生成控制器以获取详细信息。
Q: 您需要在交互模式下运行构建服务吗?
A: 不可以。然后您可以使用托管的生成控制器。
我建议您设置本地生成控制器,并在交互模式下运行生成服务器。有关详细信息,请参阅安装生成服务器和安装生成控制器。
似乎答案可能来自Visual Studio Online方面。如果有人有类似的问题,会有一个用户语音条目。
不太清楚为什么模拟器没有非交互式模式,就我个人而言,99%的时间我都不使用它的UI。有一个通用的用户语音条目,用于使Azure存储更易于单元测试。
如果您想在C#中的集成测试代码中启动Azure Storage Emulator,您可以将其放入测试初始化(启动)代码中(例如xUnit):
[Collection("Database collection")]
public sealed class IntegrationTests
{
public IntegrationTests(DatabaseFixture fixture)
{
this.fixture = fixture;
}
[Fact]
public async Task TestMethod1()
{
// use fixture.Table to run tests on the Azure Storage
}
private readonly DatabaseFixture fixture;
}
public class DatabaseFixture : IDisposable
{
public DatabaseFixture()
{
StartProcess("SqlLocalDB.exe", "create MSSQLLocalDB");
StartProcess("SqlLocalDB.exe", "start MSSQLLocalDB");
StartProcess("SqlLocalDB.exe", "info MSSQLLocalDB");
StartProcess(EXE_PATH, "start");
var client = CloudStorageAccount.DevelopmentStorageAccount.CreateCloudTableClient();
Table = client.GetTableReference("tablename");
InitAsync().Wait();
}
public void Dispose()
{
Table.DeleteIfExistsAsync().Wait();
StartProcess(EXE_PATH, "stop");
}
private async Task InitAsync()
{
await Table.DeleteIfExistsAsync();
await Table.CreateAsync();
}
static void StartProcess(string path, string arguments, int waitTime = WAIT_FOR_EXIT) =>
Process.Start(path, arguments).WaitForExit(waitTime);
public CloudTable Table { get; }
private const string EXE_PATH =
"C:\Program Files (x86)\Microsoft SDKs\Azure\Storage Emulator\AzureStorageEmulator.exe";
private const int WAIT_FOR_EXIT = 60_000;
}
[CollectionDefinition("Database collection")]
public class DatabaseCollection : ICollectionFixture<DatabaseFixture>
{
// This class has no code, and is never created. Its purpose is simply
// to be the place to apply [CollectionDefinition] and all the
// ICollectionFixture<> interfaces.
}