Verify data in tests with ASP.NET Core and EF Core in memory

Introduction

You’re writing a Web API with ASP.NET Core 2.1, and use EF Core as your ORM.
If you follow the official guidance on doing integration tests in ASP.NET Core 2.1, then you can use either an in-memory database provider, or SQLite in-memory. We are using an in-memory database provider, which is setup in our CustomWebApplicationFactory as per the current Microsoft guidelines which are these:

public class CustomWebApplicationFactory<TStartup> 
    : WebApplicationFactory<RazorPagesProject.Startup>
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureServices(services =>
        {
            // Create a new service provider.
            var serviceProvider = new ServiceCollection()
                .AddEntityFrameworkInMemoryDatabase()
                .BuildServiceProvider();

            // Add a database context (ApplicationDbContext) using an in-memory 
            // database for testing.
            services.AddDbContext<ApplicationDbContext>(options => 
            {
                options.UseInMemoryDatabase("InMemoryDbForTesting");
                options.UseInternalServiceProvider(serviceProvider);
            });

These work well when running integration tests, as it allows you to quickly setup a database for each test, independent of every other test.

The problem

I couldn’t find any guidance on how to verify the data gets written correctly to the database in an assertion. e.g. here is one of our tests. We are using the XBehave library:

[Scenario]
public void CreateApplication_ShouldReturn201()
{
    "Given a valid create application request"
      .x(() => _fixture.GivenAValidCreateApplicationRequest());
    "When an application is created"
      .x(() => _fixture.WhenAnApplicationIsCreated());
    "Then the response http status code is a 201"
      .x(() => _fixture.ThenTheResponseStatusCodeIs(HttpStatusCode.Created));
    "And the response should contain an id"
      .x(() => _fixture.ThenTheResponseShouldContainAnApplicationId());
    "And the database should contain the application"
      .x(() => _fixture.ThenTheDatabaseShouldContainTheApplication());
}

How to verify the final step – “ThenTheDatabaseShouldContainTheApplication”?

The solution

Firstly, you need to change the call to AddDbContext so that your DbContext is a Singleton (the default is Scoped).

services.AddDbContext<ApplicationDbContext>(options => 
{
    options.UseInMemoryDatabase("InMemoryDbForTesting");
    options.UseInternalServiceProvider(serviceProvider);
}, ServiceLifetime.Singleton);

Then, you can get a DbContext from the system under test by asking its service collection for one, with a helper property like so:

protected ApplicationDbContext DbContext
{
    get => TestServer.Host.Services.GetService(typeof(ApplicationDbContext)) as ApplicationDbContext;
}

Since the DbContext is a singleton it’ll be the same one the system under test used, so we can query the DbContext for it directly.

internal void ThenTheDatabaseShouldContainTheApplication()
{
    var application = DbContext.Applications.Find(ApplicationId);
    Assert.NotNull(application);
}
Advertisements

Azure Key Vault + MSI = failing Web API integration tests

Introduction

In a number of our ASP.NET Core Web APIs we’re using Azure Key Vault for keeping secrets such as connection strings and authentication credentials out of source control.

Initially we were using Azure Key Vault with a clientid and clientsecret, which are stored in appsettings.json like so:

  "KeyVault": {
    "Name": "MyApplicationDev",
    "ClientId": "abcdefg123-b292-4177-ba53-858227a9143c",
    "ClientSecret": "5m9g9cpuNc31abcJZcjkfP9/pDwJgQ+T82t/qCey7Nc="
  },

But this begs the question – what happens if the Key Vault ClientId and ClientSecret get compromised? To prevent this you can setup Key Vault to use Managed Service Identity (MSI). With that in place you don’t need to have the ClientId and ClientSecret in your appsettings.json, instead you only need the KeyVault url:

  "KeyVaultSettings": {
    "Url": "https://myapplicationdev.vault.azure.net"
  },

This is usually configured in the WebApi’s Program.cs, via something like:

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((context, builder) =>
        {
            builder.SetBasePath(context.HostingEnvironment.ContentRootPath)
                    .AddEnvironmentVariables();

            var config = builder.Build();
            var tokenProvider = new AzureServiceTokenProvider();
            var keyvaultClient = new KeyVaultClient((authority, resource, scope)
               => tokenProvider.KeyVaultTokenCallback(authority, resource, scope));
                    
            builder.AddAzureKeyVault(config["KeyVaultSettings:Url"], keyvaultClient, new DefaultKeyVaultSecretManager());
        })
        .UseStartup<Startup>();

Under the covers, this will add an AzureKeyVaultConfigurationSource to the registered list of IConfigurationBuilders (as well as do other things).

The problem

In ASP.NET 2.1 we are using the new WebApplicationFactory<T> for running integration tests against an in-memory TestServer. When you run these tests locally it’ll use your (i.e. you, the developer’s) AD credentials to authenticate against the Key Vault, and so the tests should pass if you have access to the Key Vault. However, when the tests run on the Build server (as part of your CI pipeline) then they’ll probably fail because the Build agent does not have access to the Key Vault.

My solution

Firstly, I didn’t want to change the code of the system under test (i.e. the Web API), i.e. by adding configuration to determine whether to use Key Vault or not. Although thinking about it, that might have been easier! But it feels a bit dirty to change the application code to make integration tests easier.

The approach I took was to remove the AzureKeyVaultConfigurationSource from the list of IConfigurationBuilders from the system under test, in my custom WebApplicationFactory<T>, i.e:

protected override void ConfigureWebHost(IWebHostBuilder builder)
{
    builder.ConfigureAppConfiguration((_, configurationBuilder) =>
    {
        var keyVaultSource = configurationBuilder.Sources.FirstOrDefault(cs => cs.GetType().Name == "AzureKeyVaultConfigurationSource");
        if (keyVaultSource != null)
        {
            configurationBuilder.Sources.Remove(keyVaultSource);
        }
    });
}

This way, the code in the system under test is unchanged, instead we are just changing the configuration of the TestServer prior to it starting. Hope that helps someone.

Add an authorization header to your swagger-ui with Swashbuckle

Edit July 2018: I’ve blogged a better way to do this. Add an authorization header to your swagger-ui with Swashbuckle (revisited).

Out of the box there’s no way to add an Authorization header to your API requests from swagger-ui. Fortunately (if you’re using ASP.NET), Swashbuckle 5.0 is extendable, so it’s very easy to add a new IOperationFilter to do it for us:

public class AddAuthorizationHeaderParameterOperationFilter : IOperationFilter
{
    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {
        if (operation.parameters != null)
        {
            operation.parameters.Add(new Parameter
            {
                name = "Authorization",
                @in = "header",
                description = "access token",
                required = false,
                type = "string"
            });
        }
    }
}

Now all you need to do is register it in your EnableSwagger call:

configuration
    .EnableSwagger(c =>
    {
        c.SingleApiVersion("v1", "Commerce Services - Discounts");

        foreach (var commentFile in xmlCommentFiles)
        {
            c.IncludeXmlComments(commentFile);
        }

        c.OperationFilter<ExamplesOperationFilter>();
        c.OperationFilter<AddAuthorizationHeaderParameterOperationFilter>();
    })
    .EnableSwaggerUi(config => config.DocExpansion(DocExpansion.List));

Once that’s done it’ll give you an input field where you can paste your Authorization header. Don’t forget to add the word “bearer” if you’re using a JWT token:

Edit July 2018: I’ve blogged a better way to do this. Add an authorization header to your swagger-ui with Swashbuckle (revisited).

Generating Swagger example requests with Swashbuckle

This is a follow on from my post from last year about Generating example Swagger responses.

Update May 4th 2017: I have created a new NuGet package called Swashbuckle.Examples which contains the functionality I previously described in this blog post. The code lives on GitHub.

I have also created a .NET Standard version of the NuGet package at Swashbuckle.AspNetCore.Examples, which is also on GitHub.

It can also be useful to generate example requests, and in this post I will show you how.

First, install my Swashbuckle.Examples NuGet package, or the .NET Core version Swashbuckle.AspNetCore.Examples

Now decorate your controller methods with the included SwaggerRequestExample attribute:

[Route(RouteTemplates.DeliveryOptionsSearchByAddress)]
[SwaggerRequestExample(typeof(DeliveryOptionsSearchModel), typeof(DeliveryOptionsSearchModelExample))]
[SwaggerResponse(HttpStatusCode.OK, Type = typeof(DeliveryOptionsModel), Description = "Delivery options for the country found and returned successfully")]
[SwaggerResponseExample(tHttpStatusCode.OK, typeof(DeliveryOptionsModelExample))]
[SwaggerResponse(HttpStatusCode.BadRequest, Type = typeof(ErrorsModel), Description = "An invalid or missing input parameter will result in a bad request")]
[SwaggerResponse(HttpStatusCode.InternalServerError, Type = typeof(ErrorsModel), Description = "An unexpected error occurred, should not return sensitive information")]
public async Task<IHttpActionResult> DeliveryOptionsForAddress(DeliveryOptionsSearchModel search)
{

Now implement it, in this case via a DeliveryOptionsSearchModelExample (which should implement IExamplesProvider), which will generate the example data. It should return the type you specified when you specified the [SwaggerRequestExample].

public class DeliveryOptionsSearchModelExample : IExamplesProvider
{
    public object GetExamples()
    {
        return new DeliveryOptionsSearchModel
        {
            Lang = "en-GB",
            Currency = "GBP",
            Address = new AddressModel
            {
                Address1 = "1 Gwalior Road",
                Locality = "London",
                Country = "GB",
                PostalCode = "SW15 1NP"
            },
            Items = new[]
            {
                new ItemModel
                {
                    ItemId = "ABCD",
                    ItemType = ItemType.Product,
                    Price = 20,
                    Quantity = 1,
                    RestrictedCountries = new[] { "US" }
                }
            }
        };
    }

Don’t forget to enable the ExamplesOperationFilter when you enable Swagger, as before:

configuration
    .EnableSwagger(c =>
    {
        c.OperationFilter<ExamplesOperationFilter>();
    })
    .EnableSwaggerUi();

Or if you’re using .NET Core

services.AddSwaggerGen(c =>
   {
        c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
        c.OperationFilter<ExamplesOperationFilter>();

Now that we’ve done all that, we should see the examples output in our swagger.json file, which you can get to by starting your solution and navigating to /swagger/docs/v1.

Capture

And the best part is, when you’re using swagger-ui (at /swagger/ui/index), now when you click the example request in order to populate the form, instead of getting an autogenerated request like this:

Untitled

You’ll get your desired example, like this:

Capture2

I find that having a valid request on hand is useful for smoke testing your API endpoints are working correctly.

Azure Emulator not working with SQL server alias

I just spent a few hours trying to figure out something that had me stumped.

In my local dev environment I’m building a web api which has a SQL database. The connection string for the database is an alias with a named pipe. If I set the web api as StartUp project in Visual Studio it works fine. (See this post for some tips on how to do that). But when I’d instead start the Azure emulator as the StartUp project it wouldn’t connect to the sql server, with the good ol’:

A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 26 – Error Locating Server/Instance Specified)

Eventually I figured it out. The alias was the problem, because if I changed the connectionstring to .\sqlexpress it worked fine.

Digging deeper, the problem was I had set up the alias on the “SQL Native Client 11.0 Configuration (32bit)” node, but I hadn’t added an alias on the “SQL Native Client 11.0 Configuration” node. So the fix was to create an additional Alias on the “SQL Native Client 11.0 Configuration” node.

sql

So it seems that debugging a web api locally may be a 32 bit process but debugging with the Azure emulator is a 64 bit process. Maybe?

Anyway, hope this helps someone.

Generating Swagger example responses with Swashbuckle

Update May 4th 2017: I have created a new NuGet package called Swashbuckle.Examples which contains the functionality I previously described in this blog post. The code lives on GitHub.

I have also created a .NET Standard version of the NuGet package at Swashbuckle.AspNetCore.Examples, which is also on GitHub.

Swashbuckle is a tool for generating Swagger, the API description language, from your ASP.NET Web Api solution.
Using Swashbuckle, which provides Swagger-UI, you can create pretty living documentation of your web api, like this:
swagger

Documenting the Response

In this post I am going to show you how to document the Response, and a new way to generate some response examples.

You can specify the type of response for Swashbuckle a number of ways. Consider a simple API endpoint which returns a list of Countries:

public class CountriesController : DefaultController
{
    [HttpGet]
    public async Task<HttpResponseMessage> Get()
    {
        var resource = new List<Country>
        {
            new Country {Code = "AR", Name = "Argentina"},
            new Country {Code = "BR", Name = "Brazil"},
            // etc etc omitted for brevity
        };

        return Request.CreateResponse(HttpStatusCode.OK, resource);
    }
}

One way of describing the response code and content for Swashbuckle is using a combination of XML comments, and the ResponseType attribute, like so:

/// <response code="200">Countries returned OK</response>
[HttpGet]
[ResponseType(typeof(IEnumerable<Country>))]
public async Task<HttpResponseMessage> Get()
{

However, this only allows for one type of response.

If your API method can return multiple types, i.e. in the case of an error, then you can use the new SwaggerResponse attribute:

[HttpGet]
[SwaggerResponse(HttpStatusCode.OK, Type=typeof(IEnumerable<Country>))]
[SwaggerResponse(HttpStatusCode.BadRequest, Type = typeof(IEnumerable<ErrorResource>))]
public async Task<HttpResponseMessage> Get(string lang)
{

The Swagger 2.0 spec allows for examples to be added to the Response. However, at time of writing Swashbuckle doesn’t support this. Fortunately Swashbuckle is extendible so here is a way of doing it.

Install my Swashbuckle.Examples NuGet package.

Decorate your methods with the new SwaggerResponseExample attribute:

[SwaggerResponse(HttpStatusCode.OK, Type=typeof(IEnumerable<Country>))]
[SwaggerResponseExample(HttpStatusCode.OK, typeof(CountryExamples))]
[SwaggerResponse(HttpStatusCode.BadRequest, Type = typeof(IEnumerable<ErrorResource>))]
public async Task<HttpResponseMessage> Get(string lang)

Now you’ll need to add an Examples class, which will implement IExamplesProvider to generate the example data

public class CountryExamples : IExamplesProvider
{
    public object GetExamples()
    {
        return new List<Country>
        {
            new Country { Code = "AA", Name = "Test Country" },
            new Country { Code = "BB", Name = "And another" }
        };
    }
}

And finally enable the ExamplesOperationFilter when you configure Swashbuckle’s startup.

configuration
    .EnableSwagger(c =>
    {
        c.OperationFilter<ExamplesOperationFilter>();
    })
    .EnableSwaggerUi();

Now that we’ve done all that, we should see the examples output in our swagger.json file, which you can get to by starting your solution and navigating to /swagger/docs/v1.

response

And then, when you browse the swagger-ui at /swagger/ui/index, instead of an autogenerated example like this:
response old

You’ll see your desired example, like this:
response new

Be sure to check out Part 2, where we again use Swashbuckle to generate example requests.