12
votes

I'd like to run unit / integration tests that utilise the Azure Storage Emulator rather than real storage from a Azure DevOps build.

The emulator is installed on the Hosted Build Controller as part of the Azure SDK in its usual place (C:\Program Files (x86)\Microsoft SDKs\Azure\Storage Emulator\AzureStorageEmulator.exe).

However the emulator is in the uninitialised state on the Build Controller. When trying to run the command Init from the command line, I get the following error:

This operation requires an interactive window station

Is there an known workaround for this or plans to support the emulator in Azure DevOps builds?

5
Note that in addition to the answers below, even if the VSTS hosted build agent supported interactive mode the Azure Storage Emulator still would not work due to insufficient privileges. See github.com/Microsoft/vso-agent/issues/72Livven

5 Answers

19
votes

Despite all the answers here to the contrary, I've been running the Azure Storage Emulator on a VS2017 hosted build agent for over a year.

The trick is to initialise SQL LocalDB first (the emulator uses it), and then start the emulator. You can do this with a command line task that runs:

sqllocaldb create MSSQLLocalDB
sqllocaldb start MSSQLLocalDB
sqllocaldb info MSSQLLocalDB

"C:\Program Files (x86)\Microsoft SDKs\Azure\Storage Emulator\AzureStorageEmulator.exe" start
3
votes

As already stated you can't run the Azure Storage Emulator. What you can run though is Azurite an open source alternative.

Please note: Azurite can emulate blobs, tables and queues. However I have only used the blob storage emulation in this way.

At the start of your build configuration add a nuget step that runs a custom nuget command install Azurite -version 2.2.2. Then add a command line step that runs start /b $(Build.SourcesDirectory)\Azurite.2.2.2\tools\blob.exe.

It runs on the same port as the Azure Storage Emulator so you can use the standard connection strings.

1
votes

No, the Hosted Build Controller does not run in Interactive Mode, so the emulator won't work under the environment. See Q&A in Hosted build controller for XAML builds for details.

Q: Do you need to run your build service in interactive mode?

A: No. Then you can use the hosted build controller.

I recommend you setup on-premises build controller and run the build server in Interactive Mode. Refer to Setup Build Server and Setup Build Controller for details.

1
votes

Seems like the answer is maybe from the Visual Studio Online side. There's a User Voice entry if anyone has similar issues.

Not really sure why the emulator doesn't have a non-interactive mode, personally I don't use it's UI 99% of the time. There's a general User Voice entry for making Azure Storage more unit testable.

1
votes

If you want to do start the Azure Storage Emulator right in your integration test code in C#, you can put this into your test initialization (startup) code (the example is for 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.
}