I am integration testing an ASP.NET Core app that uses Redis to store some of its state, as well as an SQL databse. My test fixture removes both of these services and replaces them with ones that are more appropriate for testing. My test classes look like this:
public class SignupTest : IClassFixture<CustomWebApplicationFactory<Program>>
where the CustomWebApplicationFactory is:
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
ServiceDescriptor sqlDescriptor = services.Single(
d => d.ServiceType ==
typeof(DbContextOptions<ApiContext>));
ServiceDescriptor redisDescriptor = services.Single(
d => d.ServiceType == typeof(IDistributedCache)
&& d.ImplementationType?.Name == "RedisCache");
services.Remove(sqlDescriptor);
services.Remove(redisDescriptor);
services.AddDbContext<ApiContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForTesting");
});
// Not sure if this is really needed as there seems to be
// a fallback IDistributedCache service present by default
// (hence the additional narrowing to Name == "RedisCache")
services.AddDistributedMemoryCache();
ServiceProvider sp = services.BuildServiceProvider();
using IServiceScope scope = sp.CreateScope();
IServiceProvider scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<ApiContext>();
var logger = scopedServices.GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();
var cache = scopedServices.GetRequiredService<IDistributedCache>();
TestUtils.SeedDbForTests(db);
TestUtils.SeedCacheForTests(cache); // Set values in cache
logger.LogInformation("Cache session: {string}", cache.GetString("session"));
});
}
}
When I debug my tests, I see in the output that the above code is executed and the keys I set in SeedCacheForTests(cache) are retrievable:
MyAPI.Test.Integration.CustomWebApplicationFactory: Information: Cache session: {
"SessionId": "session_id",
"DeviceAccountId": "logged_in_id",
"ViewerId": 10000000001
}
However, in my actual tests it seems that the very same keys I'm setting, and accessing as above, now return null.
This is quite odd since values from the in-memory database DbContext persist just fine. Moreover, if I delete the code that removes Redis or add another Redis instance instead of a DistributedMemoryCache, the tests pass and values are accessible again.
However, using Redis in the fixture pollutes my main instance of Redis, which is undesirable. I am not opposed to using a real Redis instance, so long as it's possible to keep it separate from the one my app uses at runtime -- however I'm on Windows and running Redis normally through Docker, so this could be tricky.
Would very much appreciate any help because I am completely stumped...
As an interim solution, it seems that if I instead call SeedCacheForTests in the constructor of my test class, the values are then able to be accessed:
public class ToolAuthTest : IClassFixture<CustomWebApplicationFactory<Program>>
{
private readonly HttpClient _client;
private readonly CustomWebApplicationFactory<Program> _factory;
public ToolAuthTest(CustomWebApplicationFactory<Program> factory)
{
_factory = factory;
_client = _factory.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
var cache = _factory.Services.GetRequiredService<IDistributedCache>();
TestUtils.SeedCacheForTests(cache);
}
}
However, I really would prefer a way to include this in the fixture as I am looking at adding this code to a large number of tests, and we all dislike boilerplate!
Related
I have a controller with the definition:
public async Task<ResponseDto> MethodNameController(List<string> identifiers)
{
if(!_cache.TryGetValue(key, out IDictonary<string, string> result))
{
result = await service.GetMethodService(activityContextObject, identifiers)
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromSeconds(80))
.SetAbsoluteExpiration(TimeSpan.FromSeconds(120))
.SetSize(1024);
_cache.Set(key, result, cacheEntryOptions);
}
return new ResponseDto{ //ResponseDto Object split into many fields};
}
The constructor is as below
public Controller(serviceName, httpContextAccessor, IOptionsSnapshot_config, IMemoryCache)
{
_serviceName = serviceName,
_httpContextAccessor = httpContextAccessor,
_config = IOptionsSnapshot_config
_cache = IMemoryCache
}
I'm facing difficulty in unit testing this method. I followed this blog, this SO answer and many more trynig to replicate them in my scenario and failed. I tried passing regular memory cache object(because I'm unable to mock it) but invoking controller returns error slidingExpirationTime should be positive, current time set is 00:00:00 I'm unable to set the sliding expiration in test classes.
Can anyone guide me to correct resources or tell me how I can test this method. I'm new to writing xUnit tests, so any help will be great(For learning moq etc).
The easiest would be to mock it using some libraries, like AutoFixture, Moq.
Generally you mock interfaces as follows:
var fixture = new Fixture() //creates objects that has fine logic in creating objects
.Customize(new AutoMoqCusotmization); // customize the fixture to automoq interfaces
var cacheMock = fixture.Create<Mock<IMemoryCache>>();
// use cacheMock.Setup method
another, simplier option would be to use directly Mock:
var cacheMock = new Mock<IMemoryCache>();
// use cacheMock.Setup method
If you can not use any of the above you can define dummy implmentation for the service, like:
internal class DummyMemoryCache : IMemoryCache
{
// implement interface
)
and use it in place of IMemoryCache.
I am building an API with ASP.NET core using Mongodb and i have different services user service home service and etc. I would like to know should i register every service as singleton as it is mentioned in asp.net core documention or as scoped. Link to repository https://github.com/krisk0su/apartments
UserService.cs
public class UserService
{
private readonly IMongoCollection<User> _books;
private readonly IPasswordHasher _passwordService;
public UserService(IBookstoreDatabaseSettings settings, IPasswordHasher passwordService)
{
var client = new MongoClient(settings.ConnectionString);
var database = client.GetDatabase(settings.DatabaseName);
_books = database.GetCollection<User>(settings.UsersCollectionName);
_passwordService = passwordService;
}
public List<User> Get() =>
_books
.Find(book => true)
.ToList();
public User Get(string id) =>
_books.Find(user => user.Id == id).FirstOrDefault();
public User Create(User user)
{
var password = this._passwordService.HashPassword(user.Password);
user.Password = password;
_books.InsertOne(user);
return user;
}
public void Update(string id, User bookIn) =>
_books.ReplaceOne(book => book.Id == id, bookIn);
public void Remove(User bookIn) =>
_books.DeleteOne(book => book.Id == bookIn.Id);
public void Remove(string id) =>
_books.DeleteOne(book => book.Id == id);
}
Startup.cs
services.AddSingleton<UserService>();
services.AddSingleton<BookService>();
services.AddSingleton<AuthenticationService>();
services.AddScoped<IPasswordHasher, PasswordHasher>();
The MongoDB .NET Driver reference documentation for version 2.17 explains on the Reference > Driver > Connecting page in the Mongo Client Re-use section that:
It is recommended to store a MongoClient instance in a global place, either as a static variable or in an IoC container with a singleton lifetime.
With regards to Mongo Database Re-use it doesn't mention a singleton lifetime but it does say it "is thread-safe and is safe to be stored globally", so I would interpret that to mean it can be stored safely as a singleton if that's what your implementation desired, but it doesn't need to be if you prefer another lifetime.
The implementation of IMongoDatabase provided by a MongoClient is thread-safe and is safe to be stored globally or in an IoC container.
It's the same with regards to Mongo Collection Re-use:
The implementation of IMongoCollection<TDocument> ultimately provided by a MongoClient is thread-safe and is safe to be stored globally or in an IoC container.
So again I'd interpret that to mean the choice of lifetime is up to your specific requirements.
It seems it's only the MongoClient that carries a recommendation to use a singleton lifetime.
Well its complicated.
First of all MongoClient can be singleton, so all services that uses MongoClient can be singletons as well. Its important cause singleton service cannot depends on service with shorter life (Scoped, Transient).
Now about UserService. All its dependancies is singletons and service itself don't stores any data (no fields, no props) that should live limited time or any data about particular user etc.
So it can be singleton!
But if you decided to add scoped dependancy or store any data in it:
public class UserService
{
private readonly IMongoCollection<User> users;
private readonly long userCount; //this one
public UserService(IBookstoreDatabaseSettings settings)
{
var client = new MongoClient(settings.ConnectionString);
var database = client.GetDatabase(settings.DatabaseName);
users = database.GetCollection<User>(settings.UsersCollectionName);
userCount = users.Find(_ => true).CountDocuments();
}
}
then you have to make it at least Scoped.
Btw it's much easier to have MongoClient as singleton in DI:
services.AddSingleton<IMongoClient>(s =>
new MongoClient(Configuration.GetConnectionString("MongoDb"))
);
and then use it in all services:
public class UserService
{
private readonly IMongoCollection<User> users;
public UserService(IMongoClient mongoClient)
{
var database = mongoClient.GetDatabase("DatabaseName");
users = database.GetCollection<User>(settings.UsersCollectionName);
}
}
Or if you will use just one database in your app you can move IMongoDatabase to DI as well and then you don’t need to get it every time in the service constructor.
Thank you for sharing, I am working on a MongoDB and .net core project. I have one DB with multiple collections. In start-up class
services.AddSingleton(s => { return new MongoClient(con.ConnectionString).GetDatabase(con.DatabaseName); });
My connection string and DB info are stored inside my appSetting.json.
Now in my repo, I inject
mongoDBClient.GetCollection<SomeClass>(GetCollectionNameFromAppSetting((settings.CollectionName)));
Since I am having one Database would that be ok to have that registered as a singleton? or should I change it
I have one dependency registered as follows:
interface IDependency { }
class DependencyImpl : IDependency { }
Startup:
services.AddScoped<IDependency, DependencyImpl>();
This works as intendended as I do want to reuse the same instance in the scope of my Web API requests.
However, in one background service, I'd like to tell which instance it will resolve to:
class MyBackgroundService
{
private readonly IServiceScopeFactory _scopeFactory; // set in ctor
public void DoStuff()
{
var itens = GetItens();
var dependencyInstance = new DependencyImpl();
Parallel.ForEach(itens, (item) =>
{
using(var scope = _scopeFactory.CreateScope())
{
scope.SwapDependencyForThisScopeOnly<IDependency>( () => dependencyInstance ); // something like this
var someOtherService = scope.ServiceProvider.GetRequiredService<ItemService(); // resolve subsequent services with provided dependencyInstance
someOtherService.Process(item);
}
});
}
}
I can't reuse the same Scope because ItemService (and/or it's dependencies) uses other scoped services that can't be shared. Neither I want to replace dependency resolution for the entire application.
Is it possible to do what I want here? Does it make sense?
I'm using dotnet core 2.2 with default IoC container for that matters.
Edit in reply to #Steven: DependencyImpl contains configurations for how an item will be processed. One of those includes an relatively expensive query. DependencyImpl is also injected more than once in the graph. So, currently, it reads the configuration once, cache them in private properties, and use the cached version on subsequent reads. Because I know I'll be reusing the same configuration for all itens here, I'd like to avoid reading the configuration again for each parallel execution.
My real-world dependency is more similar to this:
interface IDependency
{
Task<Configuration> GetConfigurationAsync();
}
class DependencyImpl : IDependency
{
private readonly Configuration _configuration;
private readonly DbContext _dbContext;
ctor(DbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<Configuration> GetConfigurationAsync()
{
if(_configuration is null)
{
// read configurations
}
return _configuration;
}
}
I understand that, as is, my class is not thread-safe. I'd have to force a read at the start and/or add some thread safety here.
Also, those processings used to happen during the lifetime of a web request, and the background service is the new stuff. I'd prefer to change as little of existing code as possible, because there are few tests in place, and of course time constraints from the powers-that-be.
In general, it is not a good idea to change the structure of the registered object graphs while the application is running. Not only is this hard to achieve with most containers, it is prone to suble problems that are hard to detect. I, therefore, suggest a small change in your design that change circumvents the problem you are facing.
Instead of trying to change the dependency as a whole, instead pre-populate an existing dependency with the data loaded on a a different thread.
This can be done using the following abstraction/implementation pair:
public interface IConfigurationProvider
{
Task<Configuration> GetConfigurationAsync();
}
public sealed class DatabaseConfigurationProvider : IConfigurationProvider
{
private readonly DbContext _dbContext;
public DatabaseConfigurationProvider(DbContext dbContext)
{
_dbContext = dbContext;
}
public Configuration Configuration { get; set; }
public async Task<Configuration> GetConfigurationAsync()
{
if (Configuration is null)
{
await // read configurations
}
return Configuration;
}
}
Notice the public Configuration on the DatabaseConfigurationProvider implementation, which is not on the IConfigurationProvider interface.
This is the core of the solution I'm presenting. Allow your Composition Root to set the value, without polluting your application abstractions, as application code doesn't need to overwrite the Configuration object; only the Composition Root needs to.
With this abstraction/implementation pair, the background service can look like this:
class MyBackgroundService
{
private readonly IServiceScopeFactory _scopeFactory; // set in ctor
public Task DoStuff()
{
var itens = GetItens();
// Create a scope for the root operation.
using (var scope = _scopeFactory.CreateScope())
{
// Resolve the IConfigurationProvider first to load
// the configuration once eagerly.
var provider = scope.ServiceProvider
.GetRequiredService<IConfigurationProvider>();
var configuration = await provider.GetConfigurationAsync();
Parallel.ForEach(itens, (item) => Process(configuration, item));
}
}
private void Process(Configuration configuration, Item item)
{
// Create a new scope per thread
using (var scope = _scopeFactory.CreateScope())
{
// Request the configuration implementation that allows
// setting the configuration.
var provider = scope.ServiceProvider
.GetRequiredService<DatabaseConfigurationProvider>();
// Set the configuration object for the duration of the scope
provider.Configuration = configuration;
// Resolve an object graph that depends on IConfigurationProvider.
var service = scope.ServiceProvider.GetRequiredService<ItemService>();
service.Process(item);
}
}
}
To pull this off, you need the following DI configuration:
services.AddScoped<DatabaseConfigurationProvider>();
services.AddScoped<IConfigurationProvider>(
p => p.GetRequiredService<DatabaseConfigurationProvider>());
This previous configuration registers DatabaseConfigurationProvider twice: once for its concrete type, once for its interface. The interface registration forwards the call and resolves the concrete type directly. This is a special 'trick' you have to apply when working with the MS.DI container, to prevent getting two separate DatabaseConfigurationProvider instances inside a single scope. That would completely defeat the correctness of this implementation.
Make an interface that extends IDependency and only applies to the faster implementation that you need to request, e.g., IFasterDependency. Then make a registration for IFasterDependency. That way your faster class is still an IDependency object and you won't disrupt too much existing code, but you can now request it freely.
public interface IDependency
{
// Actual, useful interface definition
}
public interface IFasterDependency : IDependency
{
// You don't actually have to define anything here
}
public class SlowClass : IDependency
{
}
// FasterClass is now a IDependencyObject, but has its own interface
// so you can register it in your dependency injection
public class FasterClass : IFasterDependency
{
}
In my Asp.Net Core App I need a singleton service that I can reuse for the lifetime of the application. To construct it, I need a DbContext (from the EF Core), but it is a scoped service and not thread safe.
Therefore I am using the following pattern to construct my singleton service. It looks kinda hacky, therefore I was wondering whether this is an acceptable approach and won't lead to any problems?
services.AddScoped<IPersistedConfigurationDbContext, PersistedConfigurationDbContext>();
services.AddSingleton<IPersistedConfigurationService>(s =>
{
ConfigModel currentConfig;
using (var scope = s.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<IPersistedConfigurationDbContext>();
currentConfig = dbContext.retrieveConfig();
}
return new PersistedConfigurationService(currentConfig);
});
...
public class ConfigModel
{
string configParam { get; set; }
}
What you're doing is not good and can definitely lead to issues. Since this is being done in the service registration, the scoped service is going to be retrieve once when your singleton is first injected. In other words, this code here is only going to run once for the lifetime of the service you're registering, which since it's a singleton, means it's only going to happen once, period. Additionally, the context you're injecting here only exists within the scope you've created, which goes away as soon as the using statement closes. As such, by the time you actually try to use the context in your singleton, it will have been disposed, and you'll get an ObjectDisposedException.
If you need to use a scoped service inside a singleton, then you need to inject IServiceProvider into the singleton. Then, you need to create a scope and pull out your context when you need to use it, and this will need to be done every time you need to use it. For example:
public class PersistedConfigurationService : IPersistedConfigurationService
{
private readonly IServiceProvider _serviceProvider;
public PersistedConfigurationService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task Foo()
{
using (var scope = _serviceProvider.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<IPersistedConfigurationDbContext>();
// do something with context
}
}
}
Just to emphasize, again, you will need to do this in each method that needs to utilize the scoped service (your context). You cannot persist this to an ivar or something. If you're put off by the code, you should be, as this is an antipattern. If you must get a scoped service in a singleton, you have no choice, but more often than not, this is a sign of bad design. If a service needs to use scoped services, it should almost invariably be scoped itself, not singleton. There's only a few cases where you truly need a singleton lifetime, and those mostly revolve around dealing with semaphores or other state that needs to be persisted throughout the life of the application. Unless there's a very good reason to make your service a singleton, you should opt for scoped in all cases; scoped should be the default lifetime unless you have a reason to do otherwise.
Although Dependency injection: Service lifetimes documentation in ASP.NET Core says:
It's dangerous to resolve a scoped service from a singleton. It may cause the service to have incorrect state when processing subsequent requests.
But in your case this is not the issue. Actually you are not resolving the scoped service from singleton. Its just getting an instance of scoped service from singleton whenever it requires. So your code should work properly without any disposed context error!
But another potential solution can be using IHostedService. Here is the details about it:
Consuming a scoped service in a background task (IHostedService)
Looking at the name of this service - I think what you need is a custom configuration provider that loads configuration from database at startup (once only). Why don't you do something like following instead? It is a better design, more of a framework compliant approach and also something that you can build as a shared library that other people can also benefit from (or you can benefit from in multiple projects).
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureAppConfiguration((context, config) =>
{
var builtConfig = config.Build();
var persistentConfigBuilder = new ConfigurationBuilder();
var connectionString = builtConfig["ConnectionString"];
persistentStorageBuilder.AddPersistentConfig(connectionString);
var persistentConfig = persistentConfigBuilder.Build();
config.AddConfiguration(persistentConfig);
});
}
Here - AddPersistentConfig is an extension method built as a library that looks like this.
public static class ConfigurationBuilderExtensions
{
public static IConfigurationBuilder AddPersistentConfig(this IConfigurationBuilder configurationBuilder, string connectionString)
{
return configurationBuilder.Add(new PersistentConfigurationSource(connectionString));
}
}
class PersistentConfigurationSource : IConfigurationSource
{
public string ConnectionString { get; set; }
public PersistentConfigurationSource(string connectionString)
{
ConnectionString = connectionString;
}
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new PersistentConfigurationProvider(new DbContext(ConnectionString));
}
}
class PersistentConfigurationProvider : ConfigurationProvider
{
private readonly DbContext _context;
public PersistentConfigurationProvider(DbContext context)
{
_context = context;
}
public override void Load()
{
// Using _dbContext
// Load Configuration as valuesFromDb
// Set Data
// Data = valuesFromDb.ToDictionary<string, string>...
}
}
I'm using SimpleInjector 4 and FluentValidation 7. My AbstractValidators have a dependency on my DbContext.
public class Validator : AbstractValidator<LocationModel>
{
public LocationModelValidator(IReadOnlyRepository repository)
{
// Check the database to see if this location is already present
RuleFor(x => x.LocationId).Must(x => !repository.Location.Any(i => i.LocationId == x)).WithMessage("A Location with this ID already exists.");
}
}
My composition root looks as follows:
var container = new Container();
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
container.Register<IReadOnlyRepository, LocationDbContext>(Lifestyle.Scoped);
container.Register<IValidatorFactory>(() => new ServiceProviderValidatorFactory(GlobalConfiguration.Configuration));
container.Register(typeof(IValidator<>), assemblies, Lifestyle.Scoped);
container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
container.Verify();
GlobalConfiguration.Configuration.DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(container);
ValidatorFactory implementation
public class ServiceProviderValidatorFactory : ValidatorFactoryBase
{
private readonly HttpConfiguration _config;
public ServiceProviderValidatorFactory(HttpConfiguration config)
{
_config = config;
}
public override IValidator CreateInstance(Type validatorType)
{
return (IValidator) _config.DependencyResolver.GetService(validatorType);
}
}
WebApiConfig.cs
// throws error: instance is requested outside the context of an active (Async Scoped) scope
// FluentValidationModelValidatorProvider.Configure(GlobalConfiguration.Configuration, x => x.ValidatorFactory = container.GetInstance<IValidatorFactory>());
FluentValidationModelValidatorProvider.Configure(config, x => x.ValidatorFactory = new ServiceProviderValidatorFactory(config));
The validation kicks in and works fine for the first API request but all subsequent API requests give the The operation cannot be completed because the DbContext has been disposed. error.
I've also tried setting up ServiceProviderValidatorFactory to use IServiceProvider approach and calling FluentValidationModelValidatorProvider.Configure(config, x => x.ValidatorFactory = new ServiceProviderValidatorFactory(container));
as shown here:
https://stackoverflow.com/a/43883455/654708
but that doesn't work either.
It looks as though once the AbstractValidators have loaded, they are cached/never disposed of but the DbContext is.
I'm guessing a hybrid lifestyle might work here (as setting the IReadOnlyRepository to a singleton lifestyle works fine) but I haven't had any luck there either.
UPDATE
Looks as though the AbstractValidator classes are Singletons. I've updated the validators and factory to be transient in the composition root but it doesn't seem to work as they are still only getting instantiated once.
container.Register<IValidatorFactory>(() => new ServiceProviderValidatorFactory(container), Lifestyle.Scoped);
container.Register(typeof(IValidator<>), assemblies, Lifestyle.Transient);
I've verified this by setting a break point inside one my AbstractValidator classes and can see that it only gets called once, i.e. on the first web api request but not on subsequent requests.
UPDATE 2
I was able to somewhat get around this problem by inject a Func<IReadOnlyRepository> repository into my AbstractValidator classes:
public class LocationModelValidator : AbstractValidator<LocationModel>
{
public LocationModelValidator(Func<IReadOnlyRepository> repository)
{
// Check the database to see if this location is already present
RuleFor(x => x.LocationId).Must(x => !repository.Invoke().Locations.Any(i => i.LocationId == x)).WithMessage("A Location with this ID already exists.");
}
}
And updating my composition root with the following:
container.RegisterSingleton<Func<IReadOnlyRepository>>(() => container.GetInstance<IReadOnlyRepository>);
I'd rather not have to do this but given I can't figure out why the AbstractValidators are always singletons, it will do for now.