开发者

Can I mock Objectresult<T> of Entity Framework Using MOQ

I am using Entity Frameowrk 4.0 and I am calling a stored procedure which returns an ObjectResult and I tried 开发者_C百科to use MOQ and have not been able to mock ObjectResult. Has anybody been able to mock ObjectResult using moq?

TIA Yaz


I have this problem too; I'm using database-first design, and the EF 4.x DbContext Generator template to generate my DbContext.

I'm modifying the context generator in the following ways:

  1. Instead of DbSet<T> for entity sets, I am returning IDbSet<T>; this allows me to use InMemoryDbSet<T> for unit testing (Google for implementations);
  2. Instead of ObjectResult<T> for stored procedures, I am returning IEnumerable<T>. Inside the virtual method created for the stored procedure, I load the ObjectResult<T> into a List<T> and return that;
  3. Finally, I extract an interface exposing the entity sets and function imports. This means I can then mock the entire DbContext for super-speedy unit tests. You should still write integration tests that test the database functionality for real, of course.


ObjectResult (according to the MSDN docs) is a sealed class as such you cannot mock it. The way Mocking libraries like Moq work is that when you do something like

Mock<Foo> fooMock = new Mock<Foo>();

It generates (using Reflection.Emit and various other magic tricks) a class that looks a little like this

public class FooMockingProxy : Foo {

    public override void This() {
        // Mocking interceptors to support Verify and Setup
    }

    public override string That() {
        // Mocking interceptors to support Verify and Setup
    }

}

i.e. It takes the class (interface) you want to Mock and subclasses it (or implements it in the case of an interface). This allows it to put in instrumentation that allows it to check if a method has been called, or returns a certain value (this supports the various Setup and Verify methods). The restrictions to this method of mocking are:-

  • Sealed classes (can't be subclassed)
  • Private members (can't be accessed from a subclass)
  • Methods or properties classes that are not virtual (and therefore cannot be overriden).

One technique you can take when approaching sealed classes is to wrap them in some kind of interface that is Mockable. Alternatively you can try and Mock an interface that the sealed class implements that only your code consumes.


ObjectResult is typically used with Linq therefore it is mainly used as IEnumerable. Even though object is sealed, you can mock it and setup IEnumerable behavior.

Here is some sample code where TResult is the stored procedure result type and TDbContext is the DbContext and it will return 1 item.

var valueEnumerator = new TResult[] { new TResult() }.GetEnumerator();

var mockStoredProcedure = new Mock<ObjectResult<TResult>();
mockStoredProcedure.Setup(x => x.GetEnumerator()).Returns(valueEnumerator);

var mockEntities = new Mock<TDbContext>();
mockEntities.Setup(x => x.[stored procedure method]()).Returns(mockStoredProcedure.Object);

You can add any values to array in example above or use any other collection (you only need the enumerator).

Give this code a try. It works for me with EF 6.1.2 and Moq 4.2


I could not find a way to mock a sealed class, and wanted to test that the parameters of a stored procedure matched the entity model. Here is my solution:

 namespace CardiacMonitoringTest
{
    [TestClass]
    public class CardiacMonitoringDataTest
    {
        [TestMethod]
        public void TestEntityStoredProcedure()
        {
            List<string> SPExceptions = new List<string>();
            SPExceptions.Add("AfibBurdenByDay");
            SPExceptions.Add("GetEventTotalsByCategory");

            EntitiesCon db = new EntitiesCon();
            foreach (MethodInfo mi in typeof(EntitiesCon).GetMethods())
            {

                string ptype = mi.ReturnType.Name;
                if (ptype.IndexOf("ObjectResult") > -1)
                {
                    List<SqlParameter> ExtractedParameters = SPListParm(ConfigurationManager.ConnectionStrings["CardiacMonitoring"].ConnectionString, mi.Name);
                ExtractedParameters = ExtractedParameters.Where(a => a.ParameterName != "@RETURN_VALUE" && a.ParameterName != "@TABLE_RETURN_VALUE").ToList();
                ParameterInfo[] EntityParameters = mi.GetParameters();
                if ((from b in SPExceptions where b.ToLower() == mi.Name.ToLower() select b).Count() > 0)
                {
                    continue;
                }
                foreach (ParameterInfo pi in EntityParameters)
                {


                        try
                        {
                            Assert.IsTrue(
                                (from a in ExtractedParameters where pi.Name.ToLower() == a.ParameterName.Replace("@", "").ToLower() select a).Count() == 1);
                        }
                        catch (Exception ex)
                        {
                            Trace.WriteLine("Failed SP:" + mi.Name + " at parameter:" + pi.Name);
                            throw (ex);
                        }
                    try
                    {
                        Assert.IsTrue(EntityParameters.Count() == ExtractedParameters.Count());
                    }
                    catch (Exception ex)
                    {
                        Trace.WriteLine("Failed SP:" + mi.Name + " on parameter count:" + EntityParameters.Count() + " with detected count as:" + ExtractedParameters.Count());
                        throw (ex);
                    }
                }
            }
            }
        }

        private List<SqlParameter> SPListParm(string ConnectionString, string SPName)
        {
            try
            {
                SqlConnection conn = new SqlConnection(ConnectionString);
                SqlCommand cmd = new SqlCommand(SPName, conn);
                cmd.CommandType = CommandType.StoredProcedure;
                conn.Open();
                SqlCommandBuilder.DeriveParameters(cmd);
                SqlParameter[] prmDetectParameters = new SqlParameter[cmd.Parameters.Count];
                cmd.Parameters.CopyTo(prmDetectParameters, 0);
                List<SqlParameter> toReturn = new List<SqlParameter>();
                toReturn.AddRange(prmDetectParameters);
                return (toReturn);
            }
            catch (Exception ex)
            {
                Trace.WriteLine("Failed detecting parameters for SP:" + SPName);
                throw (ex);
            }
        }
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜