Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To test an ASP.NET Core endpoint with a real Identity user, start the app through WebApplicationFactory, replace its production database registration with an isolated test database, and seed users through UserManager<TUser>. Then send HTTP requests through the factory’s client. If the test needs to verify sign-in itself, submit the app’s login request and keep the returned authentication cookie; if it only needs to test authorization, use a test authentication scheme instead.

Choose what the integration test needs to prove

There are two different tests that are often conflated. A sign-in test exercises the application’s actual login flow, including the Identity user store and authentication cookie. An authorization test can focus on whether an endpoint permits a principal with particular claims or roles; it can use a test authentication handler without checking the login flow.

  • Use a real Identity sign-in to test credential validation, cookie issuance, or behavior that depends on the persisted Identity user.
  • Use a test authentication scheme when the external identity provider or login mechanism is outside the test’s scope and the question is whether authorization rules allow or deny a request.

Microsoft’s Integration tests in ASP.NET Core documentation describes WebApplicationFactory<TEntryPoint> as the mechanism for creating a TestServer for integration tests. Its overview defines integration tests as exercising app components alongside supporting infrastructure such as a database, file system, or network.

Configure the test host with an isolated database

Derive a factory from WebApplicationFactory<Program> (or the application’s startup entry point). In ConfigureWebHost, remove the production registration for DbContextOptions<ApplicationDbContext> and register a test provider. The Microsoft sample uses EF Core’s in-memory provider and calls Database.EnsureCreated(); it also demonstrates SQLite with an open DataSource=:memory: connection when relational behavior is needed.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The following skeleton shows the registration pattern. Replace ApplicationDbContext and the database name with the context and isolation strategy used by your application.

public sealed class IdentityTestFactory : WebApplicationFactory<Program>
{
    private readonly string _databaseName = $"IdentityTests-{Guid.NewGuid()}";

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureServices(services =>
        {
            services.RemoveAll<DbContextOptions<ApplicationDbContext>>();
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseInMemoryDatabase(_databaseName));

            using var provider = services.BuildServiceProvider();
            using var scope = provider.CreateScope();
            var db = scope.ServiceProvider
                .GetRequiredService<ApplicationDbContext>();
            db.Database.EnsureCreated();
        });
    }
}

For SQLite, keep the in-memory database connection open for the lifetime of the factory or test; closing it discards the database. For tests involving SQL Server-specific constraints, transactions, or query behavior, use a disposable relational test database instead of treating EF Core’s in-memory provider as equivalent.

Seed users through Identity, not by inserting password data

After the host is created, open a service scope and resolve UserManager<TUser>. Create users with a deterministic, test-specific username or email and pass the password to CreateAsync. Identity then handles the password hash, normalization, validation, and persistence through the configured store.

using var scope = factory.Services.CreateScope();
var userManager = scope.ServiceProvider
    .GetRequiredService<UserManager<IdentityUser>>();

var user = new IdentityUser
{
    UserName = "integration-user@example.test",
    Email = "integration-user@example.test"
};

var result = await userManager.CreateAsync(user, "Test-password-123!");
Assert.True(result.Succeeded, string.Join("; ", result.Errors.Select(e => e.Description)));

Use the app’s actual user type instead of IdentityUser if it defines a custom Identity model. Give each test or fixture a unique database name or connection and avoid mutable shared users. If tests run in parallel, ensure they do not modify the same Identity rows.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Add a role or claim when the policy requires one

For role-based authorization, resolve RoleManager<TRole>, ensure the test role exists, and assign it with UserManager.AddToRoleAsync. For claim-based authorization, use UserManager.AddClaimAsync. Assert the result of each Identity operation so a seeding failure does not masquerade as an authorization failure.

Send requests through the test server

Create an HttpClient from the factory and call the same routes the application exposes. The test client exercises the app pipeline hosted by the test server rather than calling the controller or endpoint method directly.

Test the actual login flow

Post the application’s real login form or API credentials, using its actual route and request format. Keep the authentication cookie in the client for the subsequent protected request. This verifies the login path as well as the endpoint; merely creating an Identity row does not sign the client in.

Test authorization without testing login

When login or an external provider is not part of the test, configure a test scheme through ConfigureTestServices. Set the default authenticate and challenge schemes to the test scheme, register a custom AuthenticationHandler<AuthenticationSchemeOptions>, and have it produce the principal needed for the scenario. Send the authentication header expected by that handler. Microsoft’s integration-test example uses this pattern for authorization-focused requests.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Assert the transport result, not just the endpoint body

For an anonymous request, create the client with WebApplicationFactoryClientOptions { AllowAutoRedirect = false }. Otherwise, automatic following of a redirect to the login page can conceal the original response. Assert the response status and, when applicable, the Location header. Whether the application returns a redirect, challenge, or another result depends on its authentication configuration.

For an authenticated request, assert both the expected status and relevant response content. A request can be authenticated yet still fail authorization if the principal lacks the required role or claim.

Cover the important Identity and authorization cases

Scenario What to exercise What to assert
User creation Create through UserManager using the test store. Creation succeeds and the app can use the resulting user.
Successful login Submit valid credentials through the app’s login route. The response and subsequent request reflect the app’s signed-in behavior.
Failed login Submit invalid credentials. The login flow rejects the attempt and does not grant access.
Role or claim authorization Exercise principals with and without the required role or claim. The endpoint allows the qualifying principal and denies the other.
Disabled or deleted user Exercise the app’s behavior after the user is disabled or deleted. The result matches the application’s intended account-state handling.
Anonymous request Call the protected route without authentication, with redirects disabled. Assert the original challenge or redirect response, including Location when relevant.

Choose the provider that matches the behavior under test

The in-memory provider is convenient for a fast isolated test, but it does not reproduce every relational database behavior. Use SQLite in memory when the test needs relational behavior, keeping its connection open; use a disposable database matching production when the test depends on provider-specific constraints, transactions, or query behavior. Microsoft’s sample demonstrates both the in-memory replacement and the open SQLite connection approach.

The Identity-related packages in Microsoft’s documented dependency list include Microsoft.AspNetCore.Identity.EntityFrameworkCore, Microsoft.EntityFrameworkCore, Microsoft.EntityFrameworkCore.InMemory, and Microsoft.EntityFrameworkCore.Tools. The NuGet registry identifies Microsoft.AspNetCore.Mvc.Testing as the package supporting ASP.NET Core MVC and Minimal API integration testing with WebApplicationFactory. Microsoft Learn’s integration-testing page was last updated March 10, 2026.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.