简体   繁体   中英

Entity Framework: Can a DbContext be fully mocked?

For testing purposes, I want to create a mock of the autogenerated EF context class (DB first). In order to do that, I've generated a matching interface and made the autogenerated class implement this interface (simply by adding the interface to the T4 template). So now my EF context looks like this: class MyContext : DbContext, IContext

IContext has all the properties that MyContext and DbContext has. The next stage is to create a mock class that implements the same interface. So now I also have: class MyContextMock : IContext .

Now for the problematic part: A part of my code uses a property of DbContext named Configuration of type DbContextConfiguration (This property allows me to turn off the "auto detect changes" feature). The issue is that now I need to create an instance of DbContextConfiguration for my mock, but DbContextConfiguration has only internal constructors and it doesn't implement any interfaces. This means I can't directly create an instance of it, and I can't inherit it (thanks Microsoft).

I've successfully managed to create an instance of DbContextConfiguration using reflection, but unfortunately it is unusable. I can give more details about this attempt, but it's like going down a deep rabbit hole of internal objects instantiations and I can't see how this approach will work.

So is Entity Framework truely unmockable or is there something I can do?

In your specific case it seems like a better option to have a method/property added to your IContext, that toggles AutoDetectChanges feature:

public interface IContext {
    //....
    bool AutoDetectChanges { get; set; }
    bool LazyLoadingEnabled { get; set; }
    //etc..
}

You can probably even wrap it around to some configuration class:

public interface IContext {
    //....
    IContextOrmOptions Options { get; set; }

}

public interface IContextOrmOptions {
    bool AutoDetectChanges { get; set; }
    bool LazyLoadingEnabled { get; set; }
    //etc..
}

//real implementation
public class EntityFrameworkContextOrmOptions : IContextOrmOptions {
    private DbContext _dbContext;
    public EntityFrameworkContextOrmOptions(DbContext dbContext) {
        dbContext = _dbContext; 
    }
    public bool AutoDetectChanges {
        { get { return _dbContext.Configuration.AutoDetectChangesEnabled; } }
        { set { _dbContext.Configuration.AutoDetectChangesEnabled = value; } }
    }
}

Unfortunately, this approach kinda breaks ORM-agnosticness of your system, maybe there's a way for you to avoid this kind of code?

This will also make you have problems if you will want to mock .Entry() method, since it provides a whole lot of methods for you -- but this can be hard anyways.

It's far more simple than what you are trying to do and of course the DbContext can be mocked. This is a good article/tutorial to start with:

https://msdn.microsoft.com/en-us/library/dn314429.aspx

Assuming you are on EF 6+

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