How to test method in XUnit that needs UserManager, but uses in-memory database - unit-testing

I'm using ASP.NET Core 3.1 and XUnit for my unit tests.
I built a database context factory class that instantiates an in-memory version of my database:
public static class DbContextFactory
{
public static ApplicationDbContext CreateDbContext()
{
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
var modelBuilder = new ModelBuilder(new ConventionSet());
var dbContext = new ApplicationDbContext(options);
var onModelCreatingMethod = dbContext.GetType().GetMethod("OnModelCreating",
BindingFlags.Instance | BindingFlags.NonPublic);
onModelCreatingMethod.Invoke(dbContext,
new object[] { modelBuilder });
return dbContext;
}
}
This is the current test class I'm trying to use:
public class AdminServiceTests
{
public ApplicationDbContext context { get; set; }
public IAdminService adminService { get; set; }
public AdminServiceTests()
{
this.context = DbContextFactory.CreateDbContext();
this.adminService = new AdminService(userManager, context);
}
[Fact]
public async Task DeleteUserShouldDeleteUser()
{
// What to do ???
}
}
In order for me to test my admin service, I need to provide a user manager. It should be linked with the database I currently have created.
How can I make that happen?

You're making a common mistake of testing the framework. All your test needs to do is ensure that AdminService.DeleteUser calls UserManager.DeleteAsync. Whether or not that spirals down into actually removing the user from the database is 1) not a concern of the service and 2) an implementation detail of both ASP.NET Core Identity and EF Core, both of which have their own extensive test suites to ensure that happens.
As such, you can just use a library like Moq to create a mock of UserManager<TUser> and then do something like:
userManagerMock.Verify(x => x.DeleteAsync(user), Times.Once());
It's worth mentioning here that this also serves to point out a bit of a flaw in this kind of design. You have a dependency on ASP.NET Core Identity whether or not you put an AdminService wrapper around that. Unless your service is doing something special outside of just proxying to UserManager here (e.g. coordinating multiple actions, like maybe deleting the user triggers a notification or something), then your service is pointless, and you should just use UserManager directly. Developers make this kind of mistake constantly; abstraction for the sake of abstraction only hurts your code. It adds additional maintenance concerns, testing concerns, and obscures what the code is actually doing.

Related

ASP.NET Web API Unit Test Autofac Module with BuildManager.GetReferencedAssemblies()

Working on a project in ASP.NET Web API 2 which has Autofac as my IoC container. This project is hosted on IIS and in my Autofac module I use the following method to scan for assemblies:
var asm = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToArray();
Why?
https://docs.autofac.org/en/latest/register/scanning.html#iis-hosted-web-applications
But now we are making Unit Tests using NUnit, during my setup I register my module which uses this method. Now I receive the following exception when running my tests:
System.InvalidOperationException: 'This method cannot be called during the application's pre-start initialization phase.'
I understand why I have this exception but I don't have a clue how to make my code work in tests and for deployment environments.
Setup method of NUnit:
[TestFixture]
public abstract class ApplicationTestBase
{
[SetUp]
public override void Init()
{
var builder = new ContainerBuilder();
// If the class requires auto mapper mapping, initialize them
// We do this in order not to init them for every test => optimalisation!
if (GetType().GetCustomAttributes<RequiresAutoMapperMappingsAttribute>(false) != null)
{
builder.RegisterModule<AutoMapperModule>();
}
this.Container = builder.Build();
}
}
Do I need to create a new module specific for my Unit tests or is there another way for this?
AutoMapperTest
[RequiresAutoMapperMappings]
[TestFixture]
public class AutoMapperTests : ApplicationTestBase
{
[Test]
public void Assert_Valid_Mappings()
{
Mapper.AssertConfigurationIsValid();
}
}
UPDATE
Like Cyril mentioned: Why do you need Ioc in your unit tests? I went searching and indeed you don't have to use the Ioc in your tests. So I ditched the Ioc and initialized my mapper configuration byy doing:
Mapper.Initialize(configuration =>
{
var asm = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => a.FullName.StartsWith("ProjectWebService."));
configuration.AddProfiles(asm);
});
I would recommend separating the "how to load assemblies" logic from the "do assembly scanning and register modules logic."
Right now I'm guessing you have something like this all in one method.
public IContainer BuildContainer()
{
var asm = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToArray();
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes(asm);
var container = builder.Build();
}
Not exactly that, but something similar - the loading of assemblies is inlined and directly used.
Separate that so you can swap that logic in for testing. For example, consider allowing a parameter to be optionally passed so you can override the logic in test.
public IContainer BuildContainer(Func<IEnumerable<Assembly>> assemblyLoader = null)
{
IEnumerable<Assembly> asm = null;
if (assemblyLoader != null)
{
asm = assemblyLoader();
}
else
{
asm = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToArray();
}
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes(asm);
var container = builder.Build();
}
Your default logic will work the way you want, but then in testing you can swap in something else.
var container = BuildContainer(() => AppDomain.GetAssemblies());
There are lots of ways you can do that swap-in. It could be anything from a static property you can set somewhere to a virtual method you can override somewhere. The point is, by separating the assembly loading logic you can get the test-time behavior to work but still use the registration behavior you're after.

How to Mock an AutoMapper IMapper object in Web API Tests With StructureMap Dependency Injection?

So I've build a WebAPI from scratch, including some best practices that I've found online such as Dependency Injection and Domain<->DTO mapping using auto mapper etc.
My API Controllers now look similar to this
public MyController(IMapper mapper)
{
}
and AutoMapper Registry:
public AutoMapperRegistry()
{
var profiles = from t in typeof(AutoMapperRegistry).Assembly.GetTypes()
where typeof(Profile).IsAssignableFrom(t)
select (Profile)Activator.CreateInstance(t);
var config = new MapperConfiguration(cfg =>
{
foreach (var profile in profiles)
{
cfg.AddProfile(profile);
}
});
For<MapperConfiguration>().Use(config);
For<IMapper>().Use(ctx => ctx.GetInstance<MapperConfiguration>().CreateMapper(ctx.GetInstance));
}
I'm also building a few test cases, implementing MOQ, and this is where i feel a little unsure. whenever calling my controllers, I need to pass in an IMapper like this:
var mockMapper = new Mock<IMapper>();
var controller = new MyController(mockMapper.Object);
But then, how do i configure the IMapper to have the correct mappings? It feels redundant to recreate the same logic I've already created before to configure the Mapper. so I am wondering what is the recommended approach to do this?
That's pretty simple: if you mock IMapper and imagine it as a fully abstract concept of mapping data from one object to another, then you have to treat is an abstraction and not imply there's a real automapper behind it.
First you should not register any existing profile at all, you should instead setup IMapper.Map method to return specific object when given another object.
So for each profile used for specific method you have to do a setup, looking approximately like this:
var mockMapper = new Mock<IMapper>();
mockMapper.Setup(x => x.Map<DestinationClass>(It.IsAny<SourceClass>()))
.Returns((SourceClass source) =>
{
// abstract mapping function code here, return instance of DestinationClass
});
In this case, your test knows nothing about actual IMapper implementation - it just uses it methods to get the data you expect from actual IMapper implementation to receive.
This might me another solution
//auto mapper configuration
var mockMapper = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new AutoMapperProfile()); //your automapperprofile
});
var mapper = mockMapper.CreateMapper();
And then call then controller like so
var controller = new YourController(imapper:mapper,..otherobjects..);
This way it will serve the purpose or else if you create mock object for IMapper then it will return what you ask it to return.

Unit testing with mvc api & ninject

Sorry if this comes across as a stupid question im just not sure how to get started writing some unit tests.
I have a solution containing an api and a unit test project. The api has a repository/interface used for data access using ninject.
My question is how is my best way to unit test my api controllers. I have read a little about Moq but not sure if I need to use it as I want to test against my database.
I have read that I need to use a [TestInitialize] attribute
[TestInitialize]
public void MyTestInitialize()
{
var kernel = NinjectWebCommon.CreatePublicKernel();
kernel.Bind<BusinessController>().ToSelf();
}
My problem is my test project cant resolve CreatePublicKernel
Checking the NinjectWebCommon class in the api there is no function called CreatePublicKernel.
What am I missing here?
Ninject (or other DI library) is used only to provide dependencies into your controller's constructor. E.g. if you need BusinessController which requires two repositories, then controller should have constructor which expects these dependencies:
public BusinessController(IUserRepository userRepository,
IOrderRepository orderRepository)
{
_userRepository = userRepository;
_orderRepository = orderRepository;
}
If you want to write unit tests for your controller, you should provide mocked implementations of these repositories. Use Moq or other framework for creating mocks:
var userRepositoryMock = new Mock<IUserRepository>();
var orderRepositoryMock = new Mock<IOrderRepository>();
// setup mocks here
var controller = new BusinessController(userRepositoryMock.Object,
orderRepositoryMock.Object);
If you are writing integration tests for your controller, you should provide real implementations of these repositories, which use some real database.
var userRepository = new NHibernateUserRepository();
var orderRepository = new NHibernateOrderRepository();
// prepare some data in database here
var controller = new BusinessController(userRepository, orderRepository);
You can move controller instantiation into some method which is executed before each test (SetUp or TestInitialize method) in order to remove code duplication from your tests.
UPDATE: You also can use Ninject for integration testing. Just create Ninject module which will be used both by your real application and integration tests:
public class FooModule : NinjectModule
{
public override void Load()
{
Bind<IUserRepository>().To<NHibernateUserRepository>();
Bind<IOrderRepository>().To<NHibernateOrderRepository>();
Bind<BusinessController>().ToSelf();
}
}
Then use this module both to create kernel in NinjectWebCommon.CreateKernel method and kernel in your tests:
var kernel = new StandardKernel(new FooModule());
var controller = kernel.Get<ValuesController>();

Entitity Framework 4.1 - Code First- Unit testing data access layer

I'm a .NET developer and I'm writing tests for my data access layer. I have tests that use fake repository - I have achieved that by using Moq and Ninject.
I'm getting my head around EntityFramework 4.1 Code First model and I'd like to create a prototype for CRUD routines. It's an MVC app, so my entities won't be tracked by a context.
To me it feels wrong that I'm writing tests that will make changes to the database. I will then have to clear the database each time I want to run these tests. Is this the only way to test CRUD routines?
Thank you
How do you expect to test data access if you don't access the data? Yes data access should be tested against real database. There is very simple workaround for your problem. Make your test transactional and rollback changes at the end of the test. You can use base class like this (NUnit):
[TestFixture]
public abstract class BaseTransactionalTest
{
private TransactionalScope _scope = null;
[SetUp]
public void Initialize()
{
_scope = new TransactionalScope(...);
}
[TearDown]
public void CleanUp()
{
if (_scope != null)
{
_scope.Dispose();
_scope = null;
}
}
}

How to mock ObjectContext or ObjectQuery<T> in Entity Framework?

How to mock ObjectContext or ObjectQuery in Entity Framework?
The basic mocking frameworks can only create mocks for interfaces and for abstract classes (but only for abstract/virtual methods).
As the ObjectContext is neither abstract nor interface, it is not so easy to mock it. However, as the concrete model container is generated as partial class (if you use the designer), you can extract the methods/properties you need from it to an interface. In your code, you may use the interface only, that you can mock afterwards.
With the ObjectQuery it is a little bit more easy, as it has a base interface (e.g. IQueryable) that basically contains all the neccessary operations that you usually need (and required for LINQ). So you should expose IQueryable instead of ObjectQuery in your business logic, and you can create mock for that interface.
Other alternative is to hide all data-access related logic into a separate layer (with minimal logic), test this layer with integration tests, and mock it to be able to unit test the other layers.
There are tools (I know only TypeMock) that use the profiling hooks of .NET to generate the mocks. These tools are not limited to mock interfaces or abstract classes, but with them you can mock basically anything, including non-virtual and static methods. With such a tool you don't need to change your business logic in order to allow mocking.
Although this approach is sometimes useful, you have to be aware that extracting the dependencies to interfaces (IoC) is not only helpful for mocking, but also it reduces the dependencies between your components, that has other benefits too.
Personally I like Rhino.Mocks the best from the freeware tools, but we also use TypeMock as well, which is also a great product (but you have to pay for it).
Why can't we just create the actual context object to be used in our tests? Since we don't want our tests to affect the production database, we can always specify a connection string that points to a test database. Before running each test, construct a new context, add the data you will need in your test, proceed with the unit test, then in the test cleanup section, delete all the records that were created during the test. The only side-affect here would be that the auto-increment IDs would be used up in the test database, but since it's a test database - who cares?
I know that most answers regarding this question propose using DI/IoC designs to create interfaces for data contexts etc. but the reason I am using Entity Framework is exactly to not write any interfaces for my database connections, object models, and simple CRUD transactions. To write mock interfaces for my data objects and to write complex queryable objects to support LINQ, defeats the purpose of relying on highly-tested and reliable Entity Framework.
This pattern for unit testing is not new - Ruby on Rails has been using it for a long time and it's worked out great. Just as .NET provides EF, RoR provides ActiveRecord objects and each unit test creates the objects it needs, proceeds with the tests, and then deletes all the constructed records.
How to specify connection string for test environment? Since all tests are in their own dedicated test project, adding a new App.Config file with a connection string for the test database would suffice.
Just think of how much headache and pain this will save you.
I agree with the others you cannot really Mock ObjectContext. You should use EF DbContext because you can mock the underlying DbSet There are quite a lot of post how to do that. So I won't write how to do it. However if you absolutely must use ObjectContext (for some reason) and you want to Unit test it you can use InMemory database.
First install this Nuget package: Effort (Entity Framework Fake ObjectContext Realization Tool), which uses NMemory as the database. Install Effort.EF6 package:
PM> Install-Package Effort.EF6
using System;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;
using Effort;
public class DbContextHelper
{
//Fake object you can drop this if you are using your own EF context
private class DbObject
{
public Guid Id { get; set; }
public string Name { get; set; }
}
//Fake EF context you can switch with you own EF context
private class FakeDbContext : DbContext
{
public FakeDbContext(DbConnection connection)
: base(connection, true) { }
public virtual DbSet<DbObject> DbObjects { get; set; }
}
private FakeDbContext _dbContext;
public DbContextHelper()
{
//In memory DB connection
DbConnection effortConnection = DbConnectionFactory.CreatePersistent("TestInstanceName");
_dbContext = new FakeDbContext(effortConnection);
}
//You can expose your context instead of the DbContext base type
public DbContext DbContext => _dbContext;
public ObjectContext ObjectContext => ((IObjectContextAdapter)_dbContext).ObjectContext;
//Method to add Fake object to the fake EF context
public void AddEntityWithState(string value, EntityState entityState)
{
DbContext.Entry(new DbObject() { Id = Guid.NewGuid(), Name = value }).State = entityState;
}
}
Usage:
DbContextHelper _dbContextHelper = new DbContextHelper();
_dbContextHelper.AddEntityWithState("added", System.Data.Entity.EntityState.Added);
_dbContextHelper.AddEntityWithState("added", System.Data.Entity.EntityState.Modified);
var objs = _dbContextHelper.ObjectContext.GetObjectStateEntries(EntityState.Modified | EntityState.Added);
There you are you have your object in memory DB.