XUnit CustomWebApplicationFactory with fluent validator and dependency injection - asp.net-core

I am writing some unit tests for my AspNetCore 2.2 MVC application. I want to use CustomWebApplicationFactory by XUnit to substitute my dbcontext for an inmemory one, with this standard code:
public class TestWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
services.AddDbContext<IDemoDbContext, DemoDbContext>(options =>
{
options.UseInMemoryDatabase(Guid.NewGuid().ToString());
options.UseInternalServiceProvider(serviceProvider);
});
var provider = services.BuildServiceProvider();
using (var scope = provider.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var demoContext = (DemoDbContext)scopedServices.GetRequiredService<IDemoDbContext>();
var logger = scopedServices.GetRequiredService<ILogger<TestWebApplicationFactory<TStartup>>>();
demoContext.Database.EnsureCreated();
try
{
var seeder = new DemoDbSeeder(demoContext);
seeder.SeedAllTables();
}
catch (Exception ex)
{
logger.LogError(ex, $"An error occurred seeding the test database. Error: {ex.Message}");
}
}
});
}
}
My tests keep failing at the FluentValidation validator that needs dbcontext to validate new values against the existing ones.
public class UpdateEntityCommandValidator : AbstractValidator<UpdateEntityCommand>
{
private readonly IDemoDbContext _dbContext;
public UpdateEntityCommandValidator(IDemoDbContext dbContext)
{
_dbContext = dbContext;
RuleFor(j => j.Status).Must((j, status) => BeValidStatusTransition(j.RxJobId, status)).WithMessage("Invalid status transition.");
}
private bool BeValidStatusTransition(Guid entityId, string newStatus)
{
// Do validation against existing status in the database
}
}
While the code works flawlessly with SQL server in StartUp and dbContext gets injected. When I run the test, DbSets in the context are empty. I debugged through and can tell that seeding works and I have entities after SeedAllTables. I can only assume the problem is with dependency injection. Could someone point me in the right direction on how this issue can be solved?

Related

How to inject a service in my DbContext class and have host.MigrateDatabase() still working

I've got a working EFCore, .NET5, Blazor WASM application.
I call await host.MigrateDatabase(); in my Program.Main() to have my database always up-to-date.
public static async Task<IHost> MigrateDatabase(this IHost host)
{
using var scope = host.Services.CreateScope();
try
{
// Get the needed context factory using DI:
var contextFactory = scope.ServiceProvider.GetRequiredService<IDbContextFactory<AppDbContext>>();
// Create the context from the factory:
await using var context = contextFactory.CreateDbContext();
// Migrate the database:
await context.Database.MigrateAsync();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw;
}
return host;
}
In my AppDbContext I've overridden SaveChangesAsync() to add and update CreatedOn en UpdatedOn.
I mentioned this in DbContext.SaveChanges overrides behaves unexpected before.
I also want to fill CreatedBy and UpdatedBy with the userId.
I have an IdentityOptions class to hold the user data:
public class IdentityOptions
{
public string UserId => User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
public ClaimsPrincipal User { get; set; }
}
I've registered this class in StartUp like this:
services.AddScoped(sp =>
{
var context = sp.GetService<IHttpContextAccessor>()?.HttpContext;
var identityOptions = new IdentityOptions();
if (context?.User.Identity != null && context.User.Identity.IsAuthenticated)
{
identityOptions.User = context.User;
}
return identityOptions;
});
I inject this IdentityOptions class into several other services, without any problem.
But when I inject it in my AppDbContext:
public AppDbContext(DbContextOptions<AppDbContext> options, IdentityOptions identityOptions)
: base(options)
{
...
}
I get an error in MigrateDatabase():
"Cannot resolve scoped service 'IdentityOptions' from root provider."
I've been trying numerous options I found googling but can't find a solution that works for me.
Please advice.
Update:
services.AddDbContextFactory<AppDbContext>(
options => options.UseSqlServer(Configuration.GetConnectionString("DbConnection"),
b => b.MigrationsAssembly("DataAccess"))
#if DEBUG
.LogTo(Console.WriteLine, new [] {RelationalEventId.CommandExecuted})
.EnableSensitiveDataLogging()
#endif
);
Thanks to the great help of #IvanStoev (again), I found the answer.
Adding lifetime: ServiceLifetime.Scoped to AddDbContextFactory in Startup solved my problem.
Now I can use my IdentityOptions class in SaveChanges and automatically update my Created* and Updated* properties.

Error accessing a service that uses DbContext on my quartz job

I am making a web API using ASP.NET Core and now I am having a problem with quartz scheduled jobs. The jobs I have will access my services to update the database. After some researches, I figured how to do the dependency injection so that my jobs can access the services, here is how I overrode the job factory:
public class AspNetCoreJobFactory : SimpleJobFactory
{
IServiceProvider _provider;
public AspNetCoreJobFactory(IServiceProvider provider)
{
_provider = provider;
}
public override IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
try
{
return (IJob)this._provider.GetService(bundle.JobDetail.JobType);
}
catch(Exception e)
{
throw new SchedulerException(string.Format("Problem while instantiating job '{0}' from the AspNet Core IOC.", bundle.JobDetail.Key), e);
}
}
}
and I added this line on my startup configure:
_quartzScheduler.JobFactory = new AspNetCoreJobFactory(app.ApplicationServices);
Lastly I added those two lines on my ConfigureServices method:
services.AddSingleton<IUserService, UserService>();
services.AddTransient<BatchJobCheckContract>();
right now I am getting this exception when trying to execute the job, it seems like it's because my service uses the DbContext, how can I solve this?
Cannot consume scoped service 'RHP.data.RHPDbContext' from singleton
'RHP.data.IServices.Administration.IUserService'.
After playing around with Quartz (version 3.2.3), it looks like you do not have to write your own JobFactory to use Microsoft DI. (See ASP.NET Core Integration and Microsoft DI Integration):
Add the Quartz.AspNetCore nuget package and you can scoped services like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IScopedService, ScopedService>();
// Job has scoped dependencies, so it must be scoped as well
services.AddScoped<Job>();
services.AddQuartz(q =>
{
q.UseMicrosoftDependencyInjectionScopedJobFactory();
var jobKey = new JobKey("job");
q.AddJob<Job>(jobKey);
q.AddTrigger(t => /* ... */));
});
services.AddQuartzServer(opts => opts.WaitForJobsToComplete = true);
}
However, if you cannot use the current version of Quartz.AspNetCore, you could still
use IServiceProvider as dependency in your Job class and resolve services there:
public class Job : IJob
{
private readonly IServiceProvider _serviceProvider;
public Job(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public Task Execute(IJobExecutionContext context)
{
using var scope = _serviceProvider.CreateScope();
var scopedService = scope.ServiceProvider.GetRequiredService<IScopedService>();
// ...
}
}
Like this the Job class controls the lifetime of scoped services.
I've previously had a similar problem with background tasks, you might need to create a scope.
I've adapted this code and applied it to your use case.
public class AspNetCoreJobFactory : SimpleJobFactory
{
IServiceProvider _provider;
public AspNetCoreJobFactory(IServiceProvider provider)
{
_provider = provider;
}
public override IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
try
{
using(var serviceScope = _provider.CreateScope())
{
var services = serviceScope.ServiceProvider.
return (IJob)services.GetService(bundle.JobDetail.JobType);
}
}
catch(Exception e)
{
throw new SchedulerException(string.Format("Problem while instantiating job '{0}' from the AspNet Core IOC.", bundle.JobDetail.Key), e);
}
}
}

How to configure Automapper in Structuremap

Can someone guide me on how I configure Automapper in Structuremap? Currently, I have this service that will be using the IMapper.
public class RequestService : IRequestService
{
private readonly IMapper _mapper;
private readonly IRepositoryWrapper _repositoryWrapper;
public RequestService(IMapper mapper, IRepositoryWrapper repositoryWrapper)
{
_mapper = mapper;
_repositoryWrapper = repositoryWrapper;
}
public void GetSomething()
{
var result = _repositoryWrapper.RequestRepository.GetAll();
_mapper.Map<RequestDto>(result);
}
}
On the other hand, this is my Registry. This is also the place where I configure my automapper.
public class InfrastructureRegistry : Registry
{
public InfrastructureRegistry()
{
var mapperConfiguration = new MapperConfiguration(cfg => {
cfg.AddProfile(typeof(MapperProfile));
});
var mapper = mapperConfiguration.CreateMapper();
For<IMapper>().Use(mapper);
Scan(
scan => {
scan.TheCallingAssembly();
scan.AssemblyContainingType<IRequestService>();
scan.AssemblyContainingType<IRepositoryWrapper>();
scan.WithDefaultConventions();
});
For<IRequestService>().Use<RequestService>();
For<IRepositoryWrapper>().Use<RepositoryWrapper>();
}
}
During testing, I get this error message.
StructureMap.StructureMapConfigurationException: 'No default Instance is registered and cannot be automatically determined for type 'AutoMapper.IMapper'
I'm using the below version of:
Automapper = 10.1.1
StructureMap.WebApi2 = 3.0.4.125
StructureMap = 3.0.4.125
Hope someone can guide me on this.
TIA!

Testing ASP.NET 5 with Entity Framework 7 using in memory database

I am wanting to get ahold of the Context that I am injecting into the controllers during testing and modify the data in the "in memory" version of the database context.
So the controller looks like this
[Route("api/[controller]")]
public class TestController : Controller
{
private readonly TestContext _testContext;
public TestController(TestContext testContext)
{
_testContext = testContext;
}
[HttpGet]
public IActionResult Get()
{
return Ok(new { _testContext.Users });
}
}
The test looks like this
public class SiteTests
{
[Fact]
public async Task GetIt()
{
var server = TestServer.Create(app => { app.UseMvc(); }, services =>
{
services.AddMvc();
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<TestContext>(options => options.UseInMemoryDatabase());
services.AddScoped<TestContext, TestContext>();
});
var client = server.CreateClient();
var response = await client.GetAsync("http://localhost/api/test");
var content = await response.Content.ReadAsStringAsync();
Assert.True(response.IsSuccessStatusCode);
}
}
I would love to somehow get ahold of the context before the client gets the request and modify what data will be coming back from the database context.
I have the test project in GitHub
If you're targeting .NET Core, you won't be able to make use of any automatic mocking frameworks.
The best you can do is make all your methods in TestContext virtual, then extend it in your unit tests.
public class IntegrationTestContext : TestContext
{
// override methods here
}
You can then use
var context = new IntegrationTestContext();
services.AddInstance<TestContext>(context);
You can also capture any extra information you want in IntegrationTestContext and access it from within your test.

AutoMapper testing and dependency injection for resolvers

Im writing a test for an automapper map. One of the destination members in the map requires a value resolver, and that value resolver has service dependencies which are injected. I want to use the real implementation for the resolver (since thats part of the map im testing) but Id like to use mocks for the dependencies the resolver has.
Ofcourse I want to try to avoid using an ioc container for in my tests, but how do I easily resolve my value resolver's dependencies without one?
This is my rather simplified example, in the real case there are several resolvers with sometimes many dependencies, and I really dont like to basically implement my own dependency resolver in my tests. Should I use a lightweight ioc container?
[TestFixture]
public class MapperTest
{
private IMyService myService;
[SetUp]
public void Setup()
{
Mapper.Initialize(config =>
{
config.ConstructServicesUsing(Resolve);
config.AddProfile<MyProfile>();
});
}
public T Resolve<T>()
{
return (T) Resolve(typeof (T));
}
public object Resolve(Type type)
{
if (type == typeof(MyValueResolver))
return new MyValueResolver(Resolve<IMyService>());
if (type == typeof(IMyService))
return myService;
Assert.Fail("Can not resolve type " + type.AssemblyQualifiedName);
return null;
}
[Test]
public void ShouldConfigureCorrectly()
{
Mapper.AssertConfigurationIsValid();
}
[Test]
public void ShouldMapStuff()
{
var source = new Source() {...};
var child = new Child();
myService = MockRepository.GenerateMock<IMyService>();
myService .Stub(x => x.DoServiceStuff(source)).Return(child);
var result = Mapper.Map<ISource, Destination>(source);
result.Should().Not.Be.Null();
result.Child.Should().Be.SameInstanceAs(child);
}
}
public class MyProfile : Profile
{
protected override void Configure()
{
base.Configure();
CreateMap<ISource, Destination>()
.ForMember(m => m.Child, c => c.ResolveUsing<MyResolver>());
}
}
public class MyResolver: ValueResolver<ISource, Destination>
{
private readonly IMyService _myService;
public MyResolver(IMyService myService)
{
_myService = myService;
}
protected override Child ResolveCore(ISource source)
{
return _myService.DoServiceStuff(source);
}
}
}
Here's one solution, but basically its what iv done already:
http://groups.google.com/group/automapper-users/browse_thread/thread/aea8bbe32b1f590a/f3185d30322d8109
The suggestion is to use a service locator which are set up differently depending on test or real implementation.