High memory usage for unit tests
ArnaudB88 opened this issue · 3 comments
Hi,
We recently migrated from StructureMap to Lightinject for a big solution. This solution has +1500 unit tests and we use mstest as testing framework.
With structuremap, we used one single container for all unit tests and did the necessary overrides (mocks/stubs) before and after each test. Since Lightinject doesn't support this, we create and dispose a container for each test.
We have a dbcontext for a connection to the database, so this is registered as scoped. Therefore we create a scope on the container for each unit test and retrieve instances from that scope which are needed for the test.
Some (pseudo)code:
protected IServiceContainer Container;
protected Scope ServiceFactory;
[TestInitialize]
public void Initialize()
{
Container = new ServiceContainer();
// assembly scanning for registrations
foreach(var dllFilePath in Directory.GetFiles(AppContext.BaseDirectory, "MyCompany.MySolution.*.dll"))
{
var assembly = Assembly.LoadFrom(dllFilePath);
Container.RegisterAssembly(assembly);
}
//Create Scope
ServiceFactory = Container.BeginScope();
//Override default registrations with subbed/mocked variants
Container.Register<IBar, BarStubbed>();
}
[TestCleanup]
public void CleanUp()
{
ServiceFactory.Dispose();
Container.Dispose()
}
[TestMethod]
public async Task ExecuteAsync_Should_Work()
{
var dto = new FooDto();
await ServiceFactory.GetInstance<IFoo>().ExecuteAsync(dto);
}
Problem:
When running all +1500 unit tests, the memory usage raised to 9GB. This gave errors in devops because the azure devops build agents only have 7GB of memory.
I began to investigate and came to following conclusions:
- 1 container for all unit tests gave a constant memory usage (but tests failed because overrides after first test were not possible anymore)
- 1 container per unit test resulted in a linear raising memory usage (up to 9GB)
- limiting assembly scanning for container registrations to projects which only have a composition root (6) instead of all our projects in the solution (17) resulted in less memory usage (-38%)
- not using a scope but only a container resulted in less memory usage (-85%). The scoped registrations were overridden as singleton since each unit test has a single scope.
Some timings (averages):
duration (min) | memory (GB) | explanation | |
---|---|---|---|
1 container per test | 11.5 | 9.2 | each test: 1 container, 1 scope, assembly scanning 17 projects |
limit regs | 7.9 | 5.7 | limit assembly scanning to 6 projects, each with composition root |
no scope | 8.9 | 1.4 | no scope, only container, override scoped registration as singleton |
no scope + limit regs | 7.6 | 1.3 | combination of both above |
1 container for all | 1.6 | 0.2 | 1 shared container for all tests, contant memory usage, failing tests |
Is it possible to investigate this? Currently I'll change the code so we don't use a scope in our unit tests, but the linear increasing memory is still a concern for me.
Thanks!
Hi @ArnaudB88. This might suggest a memory leak related to the scope or services registered as scoped within the container.
Would it be possible for you to create a minimal repro "proving" the memory leak?
I will see if it's possible to make a solution which illustrates this behavior. It's not easy to reproduce problems of a big solution in a simple small solution.
If you want, I can always provide other info (eg. screenshot of memory snapshots)