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

3

22

How to mock ObjectContext or ObjectQuery in Entity Framework?

Hulton answered 6/1, 2009 at 4:11 Comment(2)
It's hard to give you a good answer to your question unless you tell us what problem you're trying to solve. What, exactly, are you trying to test?Ugric
This looks like it's been answered here: #2066296Engelbert
B
21

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).

Biggerstaff answered 6/1, 2009 at 9:35 Comment(2)
Fantastic, I spent so long trying to Mock ObjectQuery but never thought to swap out ObjectQuery for IQueryable in my Repository, I literally slapped my forehead when I read this...Scrape
Yeah... just be sure that you don't go to down the IEnumerable level in abstraction, because then LINQ starts to behave differently (the queries will be executed in-memory and not by the db server).Biggerstaff
O
3

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.

Overwind answered 5/9, 2013 at 20:44 Comment(1)
In some cases i'm sure the method you suggest is entirely appropriate. In terms of why you might want to mock: 1 - Running tests on data in memory is likely to be much quicker. 2 - Hooking up to an external resource introduces a dependency in your test making it less robust. As I say though, none of these things may be an issue for you.Annieannihilate
E
0

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.

Emeric answered 17/11, 2017 at 10:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.