Reputation: 195
I am trying to get unit testing established in a new project that I'm working on. I'm using ASP.NET Boilerplate (.NET Core, Multi Page Application) with pre-built authentication as my starting point. Since Web.Tests is not included in this template, I am attempting to create my own, pulling from what was provided in the ASP.NET Core template. I've been able to establish a Startup
class that uses InMemoryDatabase
for testing. However, the most basic of tests does not pass. I'm getting stuck on having a test user who is fully authenticated and recognized as being 'logged in'. The code under test is this:
[AbpMvcAuthorize]
public class HomeController : ProjectControllerBase
{
public ActionResult Index()
{
return View();
}
}
The test is written as such:
[Fact]
public async Task Index_Test()
{
// Added as part of suggestions made by 'https://medium.com/@zbartl/authentication-and-asp-net-core-integration-testing-using-testserver-15d47b03045a'
Client.DefaultRequestHeaders.Add("my-name", "admin");
Client.DefaultRequestHeaders.Add("my-id", "2");
// Act
var response = await GetResponseAsStringAsync(
GetUrl<HomeController>(nameof(HomeController.Index))
);
// Assert
response.ShouldNotBeNullOrEmpty();
}
The blog referenced here allowed me to provide a ClaimsPrincipal
with ClaimsIdentity
using a Middleware
class. The middleware class looks like this:
public class TestAuthenticationMiddleware
{
private readonly RequestDelegate _next;
public TestAuthenticationMiddleware(RequestDelegate next, IAuthenticationSchemeProvider schemas)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
if (context.Request.Headers.Keys.Contains("my-name"))
{
if (context.Request.Headers["my-name"].First().Equals("admin"))
{
ClaimsIdentity claimsIdentity = new ClaimsIdentity(new List<Claim>
{
new Claim(ClaimTypes.Name, "admin"),
new Claim(ClaimTypes.NameIdentifier, context.Request.Headers["my-id"].First()),
new Claim(ClaimTypes.Role, "Admin"),
new Claim("http://www.aspnetboilerplate.com/identity/claims/tenantId", "1", "int"),
new Claim("AspNet.Identity.SecurityStamp", Guid.NewGuid().ToString())
},
"Identity.Application");
ClaimsPrincipal principal = new ClaimsPrincipal(claimsIdentity);
context.User = principal;
await context.SignInAsync("Identity.Application", principal);
}
}
await _next(context);
}
}
So, what I get when I run the test is a failed test.
Shouldly.ShouldAssertException : response.StatusCode
should be
HttpStatusCode.OK
but was HttpStatusCode.Redirect
I think what is happening is that we are getting stuck in the [AbpMvcAuthorize]
functionality and are getting redirected to the login page. If I remove the AbpMvcAuthorize
from the controller, then I get a different fail state. I get a null reference error. The View
is attempting to render and is failing in a subsequent view model when GetShownLoginName()
is called:
public class SideBarUserAreaViewModel
{
public GetCurrentLoginInformationsOutput LoginInformations { get; set; }
public bool IsMultiTenancyEnabled { get; set; }
public string GetShownLoginName()
{
var userName = "<span id=\"HeaderCurrentUserName\">" + LoginInformations.User.UserName + "</span>";
if (!IsMultiTenancyEnabled)
{
return userName;
}
return LoginInformations.Tenant == null
? ".\\" + userName
: LoginInformations.Tenant.TenancyName + "\\" + userName;
}
}
I want to be able to test my controller logic, to ensure that changes to views, changes to view models, and changes to services do not inadvertently cause page load errors. Is there a way, outside of creating instances of UserManager
, LogInManager
and SignInManager
in my TestBase
class and programmatically logging in the user?
Upvotes: 1
Views: 862
Reputation: 43103
MyProjectWebTestBase
inherits AbpAspNetCoreIntegratedTestBase<TStartup>
.AbpAspNetCoreIntegratedTestBase<TStartup>
uses TestAbpSession
.TestAbpSession
ignores claims.Implement MyTestAbpSession
which falls back on ClaimsAbpSession
.
public class MyTestAbpSession : TestAbpSession
{
public ClaimsAbpSession ClaimsAbpSession { get; set; }
public MyTestAbpSession(IMultiTenancyConfig multiTenancy,
IAmbientScopeProvider<SessionOverride> sessionOverrideScopeProvider,
ITenantResolver tenantResolver)
: base(multiTenancy, sessionOverrideScopeProvider, tenantResolver)
{
}
public override long? UserId
{
get => base.UserId ?? ClaimsAbpSession.UserId; // Fallback
set => base.UserId = value;
}
}
Register it in PreInitialize
method of your MyProjectWebTestModule
.
public override void PreInitialize()
{
// ...
IocManager.IocContainer.Register(
Component.For<IAbpSession, TestAbpSession>()
.ImplementedBy<MyTestAbpSession>()
.LifestyleSingleton()
.IsDefault()
);
}
Upvotes: 2