I have a C# / ASP.NET Core 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.cs
, I 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?
CodePudding user response:
To retrieve the database you've registered with the webhost builder, you can do something like this:
using (var scope = _factory.Host.Services.CreateScope())
{
_db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
}
The CustomWebApplicationFactory
doesn't know that you call that ApplicationDbContext db
, and doesn't have a field like that.
For the other build errors:
You probably also need to create some constants or other variables for label
and isTestNet
.
BitcoinHelper
is probably in an unreferenced namespace, same with MasterWallet
. You might check that you have the right references in your test project, and that you've got a using
statement at the top of your test file referencing their namespaces. You can also use Visual Studio's right click menu to add the reference/using statement if it recognizes those types.