Search code examples
c#.net-coreintegration-testing

ASP.NET Core: reset memoryCache for Integration Tests


I have created some basic Integration tests to call my Api and see if the permissions work properly. Now I have encountered a problem where running more all of the tests one of them fails - if run seperately though, it doesnt.

The reason is, that I am using IMemoryCache to store certain permissions once a user is logged in. But for my integration tests, the permissions are stored in the cache and when I try to change them for a test they are not refreshed.

In general, is there a way to invalidate the MemoryCache for every Integration test?

One of my integrationtest class basically does this:

    public IntegrationTest(CustomWebApplicationFactory<Namespace.Startup> factory)
    {
        _factory = factory;
        _client = _factory.CreateClient();

       // init the DB here etc... 

       var response = await _client.GetAsync("api/Some/Path");

       Assert.Equal(HttpStatusCode.OK, response.StatusCode);
    }

is there a way to tell the factory not to use a cache or use a mock cache oder something like that?

Edit:

The cache is setup in my startup.cs like this:

public class Startup
{

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMemoryCache();
        
        [...]
    }
    
}

And that is injected via DependenyInjection into my controllers, like this:

private IMemoryCache _cache;
private MemoryCacheEntryOptions _cacheOptions;
const int CACHE_LIFETIME_IN_DAYS = 7;

public SomeController(IMemoryCache cache) {
    _cache = cache;
    _cacheOptions = new MemoryCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromDays(CACHE_LIFETIME_IN_DAYS));
}

and I'm using it in my controllers with _cache.TryGetValue and _cache.Set


Solution

  • As a quick fix you can try to do something like this:

    var memoryCache = _factory.Services.GetService<IMemoryCache>() as MemoryCache;
    memoryCache.Compact(1.0);
    

    When you need to reset cache.

    But I would recommend either to look into not sharing _factory between tests (though it can have some performance implications) or overwriting (like it is done in the docs with context) IMemoryCache to something that you can control outside as you need.

    UPD

    Since tests by default are not run in parrallel you can just manually register instance of MemoryCache. Something like this:

    public class CustomWebApplicationFactory<TStartup>
        : WebApplicationFactory<TStartup> where TStartup : class
    {
        internal readonly MemoryCache MemoryCache;
        public CustomWebApplicationFactory()
        {
            MemoryCache = new MemoryCache(new MemoryCacheOptions());
        }
        public void ClearCache() => MemoryCache.Compact(1.0);
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureServices(services =>
            {
                var descriptor = services.SingleOrDefault(
                    d => d.ServiceType ==
                         typeof(IMemoryCache));
                services.Remove(descriptor);
                services.AddSingleton<IMemoryCache>(MemoryCache);
            });
        }
    }
    

    And in test call factory.ClearCache():

    public void Test1()
    {
        var factory = new CustomWebApplicationFactory<Startup>();
        var memoryCache = factory.Services.GetService<IMemoryCache>() as MemoryCache;
        memoryCache.Set("test", "test");
        factory.ClearCache();
        Assert.IsFalse(memoryCache.TryGetValue("test", out var val));
    }
    

    If you will need to run tests with the same factory in parallel (though I would say better just create different factories) then you can create IMemoryCache implementation which will determine somehow (for example passing some specific header in client request) different test runs and return different instances of MemoryCache for them.