简体   繁体   中英

How to mock or substitute Entity Framework context in unit test?

I would like to unit test a business logic component in one of my projects.

BL:

public class CommunicationService
{
    IContext context;

    public CommunicationService()
    {
        var kernel = new StandardKernel(new NinjectConfig());
        context = kernel.Get<IContext>();
    }

    // This creates a file
    public void SomeMethodThatUsesIContext() { ... }
}

NinjectConfig:

class NinjectConfig : NinjectModule
{
    public override void Load()
    {
        Bind<IContext>().To<CommunicationDbContext>();
    }
}

Unit test:

[TestMethod]
public void ScheduleTransportOrder_1()
{
    communicationService = new CommunicationService();
    communicationService.SomeMethodThatUsesIContext();
    Assert.IsTrue(...) // file should be created.
}

I have another Ninject config in the unit test's project:

class FakeNinjectConfig : NinjectModule
{
    public override void Load()
    {
        Bind<IContext>().To<FakeDbContext>();
    }
}

And I want this implementation of IContext to be used in the unit test. But it still uses the original, CommunicationDbContext implementation. I believed, when I have another ninject configuration here, that will be loaded to the kernel, but I think now I misunderstood something. Could you help me with that?

The problem is that you create kernel using NinjectConfig instead of FakeNinjectConfig in CommunicationService constructor.

public class CommunicationService
{
    IContext context;

    public CommunicationService()
    {
        var kernel = new StandardKernel(new NinjectConfig());
        context = kernel.Get<IContext>();
    }

Simple way:

You can create a new constructor to inject a NinjectModule or StandardKernel .

public CommunicationService (StandardKernel kernel) 
{
    context = kernel.Get<IContext>();
}

public CommunicationService(NinjectModule injectModule):this (new StandardKernel(injectModule))
{
    context = kernel.Get<IContext>();
}

public CommunicationService():this (new NinjectConfig())
{}

And in the test you can call the constructor with FakeNinjectConfig .

[TestMethod]
public void ScheduleTransportOrder_1()
{
    communicationService = new CommunicationService(new FakeNinjectConfig ());
    communicationService.SomeMethodThatUsesIContext();
    Assert.IsTrue(...) // file should be created.
}

Or

[TestMethod]
public void ScheduleTransportOrder_1()
{
    communicationService = new CommunicationService(new StandardKernel(new FakeNinjectConfig ()));
    communicationService.SomeMethodThatUsesIContext();
    Assert.IsTrue(...) // file should be created.
}

Proper way:

But in my point of view, you should add the IContext as dependency and inject IContext resolving CommunicationService .

public class CommunicationService
{
    IContext context;

    public CommunicationService(IContext _context)
    {
       context = _context;
    }

And then, when you create an CommunicationService you should use Get method of kernel instead of a new operator. For example, instead of communicationService = new CommunicationService() you should use communicationService = Kernel.Get<CommunicationService>();

[TestMethod]
public void ScheduleTransportOrder_1()
{
    var testKernel = new StandardKernel(new FakeNinjectConfig ());
    communicationService = testKernel.Get<CommunicationService>();
    communicationService.SomeMethodThatUsesIContext();
    Assert.IsTrue(...) // file should be created.
}

Or you can inject IContext directly

[TestMethod]
public void ScheduleTransportOrder_1()
{
    communicationService = new CommunicationService(new FakeDbContext());
    communicationService.SomeMethodThatUsesIContext();
    Assert.IsTrue(...) // file should be created.
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM