Я пытаюсь использовать базу данных в памяти в asp. net core web api project для тестирования интеграции.
Для аутентификации я использую BasicAuthenticationHandler
, который проверяет имя пользователя / пароль в отношении пользователей хранится в базе данных (в БД в памяти для тестирования интеграции).
Код отлично работает для запроса Get , затем для запроса Post , но затем для следующего Get запросов выглядит как Unathorized , потому что BasicAuthenticationHandler
был вызван с пустым контекстом базы данных. Таким образом, проблема возникает только после Post запроса.
Вот код:
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<UsersDbContext>(opt => opt.UseInMemoryDatabase("UsersDb"));
services.AddControllers();
// configure basic authentication
services.AddAuthentication("BasicAuthentication")
.AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
BasicAuthenticationHandler.cs
public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
private readonly UsersDbContext _context;
public BasicAuthenticationHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock,
UsersDbContext context)
: base(options, logger, encoder, clock)
{
_context = context;
}
public async Task<User> Authenticate(string username, string password)
{
// !!! SOMETIMES _context is empty here!!!!!
var user = await Task.Run(() => _context.Users.SingleOrDefault(x => x.Username == username && x.Password == password));
return user;
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
// ... parsing
user = await Authenticate(username, password);
// ... preparing
return AuthenticateResult.Success(ticket);
}
}
UsersController.cs
public class UsersController : ControllerBase
{
private readonly UserDbContext _context;
public UsersController(UserDbContext context)
{
_context = context;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<User>>> GetUsers()
{
return await _context.Users.ToListAsync();
}
[HttpPost]
public async Task<ActionResult<User>> PostUser(User user)
{
_context.Users.Add(user);
await _context.SaveChangesAsync();
return CreatedAtAction("GetUser", new { id = user.UserId }, user);
}
}
Интеграционный тест использует CustomWebApplicationFactory и выглядит следующим образом:
CustomWebApplicationFactory.cs:
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
services.AddDbContext<UserDbContext>(options =>
{
options.UseInMemoryDatabase("UsersDb");
options.UseInternalServiceProvider(serviceProvider);
});
var sp = services.BuildServiceProvider();
using (var scope = sp.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var appDb = scopedServices.GetRequiredService<UserDbContext>();
var logger = scopedServices.GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();
appDb.Database.EnsureCreated();
// Seed the database with some specific test data.
if (!appDb.Users.Any())
{
appDb.Users.Add(new User() { Username = "admin", Password = "admin" });
appDb.SaveChanges();
}
}
});
}
}
UsersControllerTests.cs
public class UsersControllerTests
{
CustomWebApplicationFactory<Startup> factory = new CustomWebApplicationFactory<Startup>();
private HttpClient _client;
[SetUp]
public void Setup()
{
_client = factory.CreateClient();
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
AuthenticationSchemes.Basic.ToString(),
Convert.ToBase64String(Encoding.ASCII.GetBytes($"admin:admin"))
);
}
[Test]
public async Task Test1Async()
{
var httpResponse = await _client.GetAsync("/api/Users");
httpResponse.EnsureSuccessStatusCode(); // works
// add a user
var json = JsonConvert.SerializeObject(new User()
{
Username = "user1",
Password = "pass1"
});
var httpContent = new StringContent(json, Encoding.UTF8, "application/json");
httpResponse = await _client.PostAsync("/api/Users/", httpContent);
httpResponse.EnsureSuccessStatusCode(); // works
// check users
httpResponse = await _client.GetAsync("/api/Users");
httpResponse.EnsureSuccessStatusCode(); // !!!Unathorized!!!!
...