asp.net – C# Can’t Access An In Memory Database Created for Integration Tests

I have a C# MVC app for which I’m writing integration tests. I’ve been following https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-6.0 and I created a CustomWebApplicationFactory like this in the following file:

CustomWebApplicationFactory.cs

using System;
using System.Linq;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using amaranth.Data;

namespace amaranth.Tests
{
    #region snippet1
    public class CustomWebApplicationFactory<TStartup>
        : WebApplicationFactory<TStartup> where TStartup: class
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureServices(services =>
            {
                var descriptor = services.SingleOrDefault(
                    d => d.ServiceType ==
                        typeof(DbContextOptions<ApplicationDbContext>));

                services.Remove(descriptor);

                services.AddDbContext<ApplicationDbContext>(options =>
                {
                    options.UseInMemoryDatabase("InMemoryDbForTesting");
                });
                services.AddAntiforgery(t =>
                {
                    t.Cookie.Name = AntiForgeryTokenExtractor.AntiForgeryCookieName;
                    t.FormFieldName = AntiForgeryTokenExtractor.AntiForgeryFieldName;
                });

                var sp = services.BuildServiceProvider();

                using (var scope = sp.CreateScope())
                {
                    var scopedServices = scope.ServiceProvider;
                    var db = scopedServices.GetRequiredService<ApplicationDbContext>();
                    var logger = scopedServices
                        .GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();

                    db.Database.EnsureCreated();
                }
            });
        }
    }
    #endregion
}

I also have the following helper files:

Helpers/HtmlHelpers.cs

Helpers/using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using AngleSharp;
using AngleSharp.Html.Dom;
using AngleSharp.Io;

namespace amaranth.Tests.Helpers
{
    public class HtmlHelpers
    {
        public static async Task<IHtmlDocument> GetDocumentAsync(HttpResponseMessage response)
        {
            var content = await response.Content.ReadAsStringAsync();
            var document = await BrowsingContext.New()
                .OpenAsync(ResponseFactory, CancellationToken.None);
            return (IHtmlDocument)document;

            void ResponseFactory(VirtualResponse htmlResponse)
            {
                htmlResponse
                    .Address(response.RequestMessage.RequestUri)
                    .Status(response.StatusCode);

                MapHeaders(response.Headers);
                MapHeaders(response.Content.Headers);

                htmlResponse.Content(content);

                void MapHeaders(HttpHeaders headers)
                {
                    foreach (var header in headers)
                    {
                        foreach (var value in header.Value)
                        {
                            htmlResponse.Header(header.Key, value);
                        }
                    }
                }
            }
        }
    }
}

Helpers/HttpClientExtensions.cs

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using AngleSharp.Html.Dom;
using Xunit;

namespace amaranth.Tests.Helpers
{
    public static class HttpClientExtensions
    {
        public static Task<HttpResponseMessage> SendAsync(
            this HttpClient client,
            IHtmlFormElement form,
            IHtmlElement submitButton)
        {
            return client.SendAsync(form, submitButton, new Dictionary<string, string>());
        }

        public static Task<HttpResponseMessage> SendAsync(
            this HttpClient client,
            IHtmlFormElement form,
            IEnumerable<KeyValuePair<string, string>> formValues)
        {
            var submitElement = Assert.Single(form.QuerySelectorAll("[type=submit]"));
            var submitButton = Assert.IsAssignableFrom<IHtmlElement>(submitElement);

            return client.SendAsync(form, submitButton, formValues);
        }

        public static Task<HttpResponseMessage> SendAsync(
            this HttpClient client,
            IHtmlFormElement form,
            IHtmlElement submitButton,
            IEnumerable<KeyValuePair<string, string>> formValues)
        {
            foreach (var kvp in formValues)
            {
                var element = Assert.IsAssignableFrom<IHtmlInputElement>(form[kvp.Key]);
                element.Value = kvp.Value;
            }

            var submit = form.GetSubmission(submitButton);
            var target = (Uri)submit.Target;
            if (submitButton.HasAttribute("formaction"))
            {
                var formaction = submitButton.GetAttribute("formaction");
                target = new Uri(formaction, UriKind.Relative);
            }
            var submission = new HttpRequestMessage(new HttpMethod(submit.Method.ToString()), target)
            {
                Content = new StreamContent(submit.Body)
            };

            foreach (var header in submit.Headers)
            {
                submission.Headers.TryAddWithoutValidation(header.Key, header.Value);
                submission.Content.Headers.TryAddWithoutValidation(header.Key, header.Value);
            }

            return client.SendAsync(submission);
        }
    }
}

The app I’m testing is called amaranth and my testing project is called amaranth.Tests. In the amaranth controller AdminController.csI am trying to test the function CreateMasterWallet which looks like this:

[HttpPost]
public IActionResult CreateMasterWallet(bool isTestNet, string label)
{
    _db.MasterWallets.Add(new MasterWallet
    {
        Label = label,
        Address = BitcoinHelper.CreatePrivateKey(isTestNet),
        IsTestNet = isTestNet
    });

    _db.SaveChanges();

    return RedirectToAction(nameof(MasterWalletList));
}

I simply need to write a test to make sure that a master wallet is successfully created and added to my in memory database but I don’t know how to access my in memory database for the test. This was my best attempt at writing a test that checks to see if a master wallet is being successfully added to the database:

[Fact]
public void CreateMasterWalletTest()
{
    var _db = _factory.db;
    _db.MasterWallets.Add(new MasterWallet
    {
        Label = label,
        Address = BitcoinHelper.CreatePrivateKey(isTestNet),
        IsTestNet = isTestNet
    });
    Assert.True(_db.Count() > 0);
}

It fails with this error:

/path/to/directory/amaranthPost052422Dir/amaranth.Tests/IntegrationTests/AdminControllerTests.cs(18,32): error CS1061: 'CustomWebApplicationFactory<Startup>' does not contain a definition for 'db' and no accessible extension method 'db' accepting a first argument of type 'CustomWebApplicationFactory<Startup>' could be found (are you missing a using directive or an assembly reference?) [/path/to/directory/amaranthPost052422Dir/amaranth.Tests/amaranth.Tests.csproj]
/path/to/directory/amaranthPost052422Dir/amaranth.Tests/IntegrationTests/AdminControllerTests.cs(19,39): error CS0246: The type or namespace name 'MasterWallet' could not be found (are you missing a using directive or an assembly reference?) [/path/to/directory/amaranthPost052422Dir/amaranth.Tests/amaranth.Tests.csproj]
/path/to/directory/amaranthPost052422Dir/amaranth.Tests/IntegrationTests/AdminControllerTests.cs(21,25): error CS0103: The name 'label' does not exist in the current context [/path/to/directory/amaranthPost052422Dir/amaranth.Tests/amaranth.Tests.csproj]
/path/to/directory/amaranthPost052422Dir/amaranth.Tests/IntegrationTests/AdminControllerTests.cs(22,27): error CS0103: The name 'BitcoinHelper' does not exist in the current context [/path/to/directory/amaranthPost052422Dir/amaranth.Tests/amaranth.Tests.csproj]
/path/to/directory/amaranthPost052422Dir/amaranth.Tests/IntegrationTests/AdminControllerTests.cs(22,58): error CS0103: The name 'isTestNet' does not exist in the current context [/path/to/directory/amaranthPost052422Dir/amaranth.Tests/amaranth.Tests.csproj]
/path/to/directory/amaranthPost052422Dir/amaranth.Tests/IntegrationTests/AdminControllerTests.cs(23,29): error CS0103: The name 'isTestNet' does not exist in the current context [/path/to/directory/amaranthPost052422Dir/amaranth.Tests/amaranth.Tests.csproj]

So how do I write a test to ensure that CreateMasterWallet is successfully creating a new MasterWallet and saving to the database?

Leave a Comment