Home > front end >  C# ASP.NET MVC : I'm trying to write a test to see if registering a new user succeeds
C# ASP.NET MVC : I'm trying to write a test to see if registering a new user succeeds

Time:05-30

I have an ASP.NET MVC app with a basic Register page. This is the specific function that register's new users.

Register.cshtml.cs:

public async Task<IActionResult> OnPostAsync()
{
        var returnUrl = Url.Content("~/Home/PostRegister");

        if (ModelState.IsValid)
        {
            var user = new IdentityUser { UserName = Input.Email, Email = Input.Email };
            var result = await _userManager.CreateAsync(user, Input.Password);
            if (result.Succeeded)
            {
                _logger.LogInformation("User created a new account with password.");

                var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                var callbackUrl = Url.Page(
                    "/Account/ConfirmEmail",
                    pageHandler: null,
                    values: new { userId = user.Id, code = code },
                    protocol: Request.Scheme);

                await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                    $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

                await _signInManager.SignInAsync(user, isPersistent: false);
                return LocalRedirect(returnUrl);
            }

            foreach (var error in result.Errors)
            {
                ModelState.AddModelError(string.Empty, error.Description);
            }
        }

        // If we got this far, something failed, redisplay form
        return Page();
}

This is the test that I wrote for it in my integration test:

[Fact]
public async Task DoesRegisterSucceed()
{
        // Arrange
        var client = _factory.CreateClient(
            new WebApplicationFactoryClientOptions
            {
                AllowAutoRedirect = false
            });
        
        var postRequest = new HttpRequestMessage(HttpMethod.Post, "/Identity/Account/Register");
        var formModel = new Dictionary<string, string>
        {
            { "Email", "[email protected]" },
            { "Password", "pas3w0!rRd" },
            { "ConfirmPassword", "pas3w0!rRd" },
        };

        postRequest.Content = new FormUrlEncodedContent(formModel);
        var response = await client.SendAsync(postRequest);

        response.EnsureSuccessStatusCode();

        //var responseString = await response.Content.ReadAsStringAsync();
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}

I get this error when I run the test.

Failed amaranth.Tests.AuthTests.Get_ClaimAdminIsReturnedForFirstRegistered [505 ms]
Error Message:
System.Net.Http.HttpRequestException : Response status code does not indicate success: 400 (Bad Request).

Stack Trace:

at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
at amaranth.Tests.AuthTests.Get_ClaimAdminIsReturnedForFirstRegistered() in /path/to/dir/amaranth.Tests/IntegrationTests/AuthTests.cs:line 102
--- End of stack trace from previous location ---

How can I make this test or write a new test that ensures registration succeeds?

Update
In case it's relevant this is my CustomWebApplicationFactory.cs file that's I'm using:

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");
                });

                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
}

CodePudding user response:

The test is failing because you don't include an antiforgery cookie and verification token in the POST, hence the 400 status code. From this page in the docs:

Any POST request to the SUT must satisfy the antiforgery check that's automatically made by the app's data protection antiforgery system. In order to arrange for a test's POST request, the test app must:

  1. Make a request for the page.
  2. Parse the antiforgery cookie and request validation token from the response.
  3. Make the POST request with the antiforgery cookie and request validation token in place.
  • Related