7
votes

I have added a test project for my core 1.0 application. Inside of which I have repositories that use dbContext in constructor.

How can I get access to this dbContext inside of test project and inject it properly?

This is my ApplicationDbContext class:

    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
        }

        public DbSet<Category> Categories { get; set; }
        public DbSet<Product> Products { get; set; }
        public DbSet<ShoppingCartItem> ShoppingCartItems { get; set; }
        public DbSet<Order> Orders { get; set; }
        public DbSet<OrderDetail> OrderDetails { get; set; }
    }

and this is my unit test method:

[Fact]
public void ProductsGetAll()
        {
        //here i need to get acces to dbContext and inject it below

            ProductRepository productRepository = new ProductRepository(dbContext);
            CategoryRepository categoryRepository = new CategoryRepository(dbContext);
            ProductController productController = new ProductController(productRepository, categoryRepository);
        }

UPDATE

I have added context in the test constructor:

ApplicationDbContext dbContext;

    public UnitTest1()
    {
        var options = new DbContextOptionsBuilder<ApplicationDbContext>()
             .UseInMemoryDatabase(databaseName: "FakeDatabase")
             .Options;

        dbContext = new ApplicationDbContext(options);
    }

and my test method:

    [Fact]
        public void ProductsGetAll()
        {
            IProductRepository productRepository = new ProductRepository(dbContext);
            ICategoryRepository categoryRepository = new CategoryRepository(dbContext);
            ProductController productController = new ProductController(productRepository, categoryRepository);

            var result = productController.List("Pizza");

            var contentResult = result as ViewResult;
            var final = contentResult != null;
            Assert.False(final, "Shouldnt be null");

but now I have an error:

Message: System.NullReferenceException : Object reference not set to an instance of an object.
2
What exactly are you testing? If it's ProductController then you should mock the repositories for unit test. On the other hand if you're doing an integration test then you'll have to point at an actually DB and if you want that to work on local machines and build servers you'll likely want to write code to generate a temp db to use. The alternative is to keep connection string info in your config to point at the location of local and build server DBs to test against. But then you'll have to be careful about tests mutating the DB and what that means when the tests are run multiple times.juharr
Usually what you do in unit tests is that you create a mock of your database as you do not want to test the database in your unit tests that is what your integration tests is used for. If you mock your database and entities you can simply have a InitializeTest method that initialises your testcontext and mock entities. I recommend using Moqi regular
did you consider using a in memory database for testing and injecting a context using this in your test? see docs.microsoft.com/en-us/ef/core/miscellaneous/testing/…nozzleman
is there any option to use only one context for every test method?user9187119
Unit tests should not be dealing with data (e.g. DbContext). That is an internal aspect and does not pertain to logic. It is your repositories that should be mocked and that can be done with interfaces.JuanR

2 Answers

2
votes

I create an interface, and place all DbSets inside it.

public class ApplicationDbContext : 
   IdentityDbContext<ApplicationUser>,  IDbContext
{
    ...
    public DbSet<Category> Categories { get; set; }
    ...
}

public interface IDbContext
{
   DbSet<Category> Categories { get; set; }
   ...
}

My repository depends on an interface instead of DbContext.

public class UserRepository : IUserRepository
{
   private readonly IDbContext _context;

   public UserRepository(IDbContext context)
   {
      _context = context;
   }
}

Then, I mock IDbContext like this instead of actual ApplicationDbContext.

var mockSet = MockHelper.GetMockDbSet(_users);
var mockDbContext = new Mock<IDbContext>();
mockDbContext.Setup(x => x.Users).Returns(mockSet.Object);
varmockDbContext = mockDbContext.Object;

Update

Based on question, you are trying to mock the repositories.

If so, I suggest you to create interfaces and implement those interfaces. Then inject those interfaces to the constructor instead of injecting concrete classes. E.g. use IProductRepository instead of ProductRepository.

You can then simply mock like this -

var mockProductRepository = new Mock<IProductRepository>();
mockProductRepository.Setup(x => x.GetAllProductsAsync()).ReturnsAsync(_products);
var productController = new ProductController(mockProductRepository.Object,...);
2
votes

Im not a .NET Core Person, but I guess you could try using an In-Memory Database for Unit Testing.

In older Versions of EntityFramework, you could have used Effort, EF Core seems to have solved this oob. Im not sure if this would work right away, since I have never tried this out by myself (again, not a .net core guy), but you should get started like that:

First, add the package Microsoft.EntityFrameworkCore.InMemory.
Then Create the Context and configure it to use the In-Memory-DB

var options = new DbContextOptionsBuilder<ApplicationDbContext>()
    .UseInMemoryDatabase(databaseName: "pick_or_generate_a_unique_name_here")
    .Options;

var context = new ApplicationDbContext(options);

Finally, inject the context into your repositories and use them in your tests

var productRepository = new ProductRepository(context);
var categoryRepository = new CategoryRepository(context);
var productController = new ProductController(productRepository, categoryRepository);

See this blogpost and MSDN for further information.