Skip to content

Instantly share code, notes, and snippets.

@zetroot
Last active August 7, 2023 21:33
Show Gist options
  • Save zetroot/b26fc6ea939403538f7c0fc7edc6b85f to your computer and use it in GitHub Desktop.
Save zetroot/b26fc6ea939403538f7c0fc7edc6b85f to your computer and use it in GitHub Desktop.
Middleware lifetime tests
using FluentAssertions;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using MiddlewareApp;
using NUnit.Framework;
namespace MiddlewareAppTests;
public class AppFac : WebApplicationFactory<Program>
{
private readonly string _lifetime;
public AppFac(string lifetime)
{
_lifetime = lifetime;
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseSetting("MIDDLEWARE_LIFETIME", _lifetime);
base.ConfigureWebHost(builder);
}
}
public class MiddlewareLifetimeTests
{
[Test]
public async Task Sut_WhenMiddlewareIsTransient_AlwaysReturns1()
{
//arrange
var factory = new AppFac("transient");
var client = factory.CreateClient();
var results = new List<int>();
//act
for (int i = 0; i < 5; ++i)
{
var response = await client.GetAsync("/");
var contents = await response.Content.ReadAsStringAsync();
results.Add(int.Parse(contents));
}
// assert
results.Should().AllSatisfy(x => x.Should().Be(1));
}
[Test]
public async Task Sut_WhenMiddlewareIsScoped_AlwaysReturns1()
{
//arrange
var factory = new AppFac("scoped");
var client = factory.CreateClient();
var results = new List<int>();
//act
for (int i = 0; i < 5; ++i)
{
var response = await client.GetAsync("/");
var contents = await response.Content.ReadAsStringAsync();
results.Add(int.Parse(contents));
}
// assert
results.Should().AllSatisfy(x => x.Should().Be(1));
}
[Test]
public async Task Sut_WhenMiddlewareIsSingleton_ReturnsDifferentValues()
{
//arrange
var factory = new AppFac("singleton");
var client = factory.CreateClient();
var results = new List<int>();
//act
for (int i = 0; i < 5; ++i)
{
var response = await client.GetAsync("/");
var contents = await response.Content.ReadAsStringAsync();
results.Add(int.Parse(contents));
}
// assert
results.Should().SatisfyRespectively(
x => x.Should().Be(1),
x => x.Should().Be(2),
x => x.Should().Be(3),
x => x.Should().Be(4),
x => x.Should().Be(5)
);
}
}
namespace MiddlewareApp;
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
var lifetime = builder.Configuration.GetValue<string>("MIDDLEWARE_LIFETIME") switch
{
"singleton" => ServiceLifetime.Singleton,
"scoped" => ServiceLifetime.Scoped,
"transient" => ServiceLifetime.Transient,
_ => throw new InvalidOperationException("Service lifetime unrecognized")
};
builder.Services.Add(new(typeof(CountMiddleware), typeof(CountMiddleware), lifetime));
var app = builder.Build();
app.UseMiddleware<CountMiddleware>();
app.MapGet("/", () => "It's wednesday, my dudes");
await app.RunAsync();
}
}
public class CountMiddleware : IMiddleware
{
private int _counter = 0;
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
++_counter;
context.Response.StatusCode = 200;
await using var sw = new StreamWriter(context.Response.Body);
await sw.WriteLineAsync(_counter.ToString());
await sw.FlushAsync();
}
}
@zetroot
Copy link
Author

zetroot commented Aug 7, 2023

Простой тест жизненного цикла миддлвари. Transient и Scoped явно не сохраняют свое состояние - инстанциируются каждый раз на запрос.
Siтgleton - наоборот, сохраняет и увеличивает значение счетчика на каждом запросе, то есть она инстанциируется один раз за время жизни программы.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment