用Moq模拟实体框架6 ObjectResult [英] Mocking Entity Framework 6 ObjectResult with Moq

查看:85
本文介绍了用Moq模拟实体框架6 ObjectResult的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何使用Moq模拟Entity Framework 6 ObjectResult,以便可以对依赖EF数据库连接的代码进行单元测试?

已经阅读了许多这样的问题和答案,并且从我所阅读的内容中收集了很多知识,我实现了我认为是一种相当优雅的解决方案,并认为我应该分享它,因为这里的社区帮助了我到达那里.因此,我将继续回答这个问题,并可能使自己对某些嘲弄(双关语意):

解决方案

首先,ObjectResult没有公共的无参数构造函数,因此有必要首先为ObjectResult创建一个可测试的包装器. @forsvarir( https://stackoverflow.com/users/592182/forsvarir )的答案让我开始思考正确地遵循这些原则( EF6-无法模拟以下项的返回值单元测试的ObjectResult< T> ):

using System.Data.Entity.Core.Objects;

namespace MyNamespace.Mocks
{
    public class TestableEfObjectResult<T> : ObjectResult<T> { }
}

当然,需要模拟DbContext.然后需要设置您的方法以返回适当的模拟枚举器.为了方便起见,我创建了一种方法来帮助创建模拟EF结果,以防止测试代码变得混乱和多余.它可以存在于您要进行测试的一些实用程序类中,尽管我只是在此处将其作为私有方法包括在内.这里的关键是,调用GetEnumerator时,模拟对象结果需要返回一个枚举数:

namespace MyNamespace.Mocks
{
    public class MockSomeDbEntities
    {
        public static Mock<SomeDbEntities> Default
        {
            get
            {
                var mockSomeDbEntities = new Mock<SomeDbEntities>();

                mockSomeDbEntities
                  .Setup(e => e.SomeMethod(It.IsAny<int>()))
                  .Returns(MockEfResult(Enumerators.SomeCollection).Object);

                return mockSomeDbEntities;
            }
        }

        private static Mock<TestableEfObjectResult<T>> MockEfResult<T>(Func<IEnumerator<T>> enumerator) where T : class 
        {
            var mock = new Mock<TestableEfObjectResult<T>>();
            mock.Setup(m => m.GetEnumerator()).Returns(enumerator);
            return mock;
        }
    }
}

我创建的用于枚举该枚举数的Enumerators类看起来就像这样.在此示例中,我使用了伪造的枚举器来创建5行数据:

using System;
using System.Collections.Generic;

namespace MyNamespace.FakeData
{
    public static class Enumerators
    {
        public static IEnumerator<Some_Result> SomeCollection()
        {
            yield return FakeSomeResult.Create(1);
            yield return FakeSomeResult.Create(2);
            yield return FakeSomeResult.Create(3);
            yield return FakeSomeResult.Create(4);
            yield return FakeSomeResult.Create(5);
        }
    }
}

而且,正如您所看到的,这仅依赖于创建每个伪数据行的类:

namespace MyNamespace.FakeData
{
    public static class FakeSomeResult
    {
        public static Some_Result Create(int id)
        {
            return new Some_Result
            {
                Id = id,
            };
        }
    }
}

能够在此级别上进行模拟实际上使我能够进行BDD,并且仅模拟或伪造外围设备,而从不模拟或伪造我的代码,因此可以获得完整的测试范围. /p>

希望这对像我一样正在寻找一种不错的简洁方法来模拟Entity Framework 6的人有所帮助.

How can I mock the Entity Framework 6 ObjectResult with Moq so that I can unit test my code that relies on an EF database connection?

Having read numerous questions and answers along these lines, and gleaned many nuggets from what I've read, I've implemented what I feel is a reasonably elegant solution and felt that I should share it, since the community here helped me get there. Thus, I'll proceed to answer this question, and potentially open myself up to some mockery (pun intended):

解决方案

First of all, ObjectResult does not have a public parameterless constructor, thus it is necessary first to create a testable wrapper for ObjectResult. The answer by @forsvarir (https://stackoverflow.com/users/592182/forsvarir) in this post got me thinking correctly along these lines (EF6 - Cannot Mock Return Value for ObjectResult<T> for Unit Test):

using System.Data.Entity.Core.Objects;

namespace MyNamespace.Mocks
{
    public class TestableEfObjectResult<T> : ObjectResult<T> { }
}

Of course, the DbContext needs to be mocked. Your method then needs to be set up to return the appropriate mocked enumerator. For convenience, I created a method to help in the creation of the Mock EF Results to keep my test code from getting cluttered and redundant. This can live in some utilitarian class that you have for your tests, though I just included it as a private method here. The key thing here being that the mock object result needs to return an enumerator when GetEnumerator is called:

namespace MyNamespace.Mocks
{
    public class MockSomeDbEntities
    {
        public static Mock<SomeDbEntities> Default
        {
            get
            {
                var mockSomeDbEntities = new Mock<SomeDbEntities>();

                mockSomeDbEntities
                  .Setup(e => e.SomeMethod(It.IsAny<int>()))
                  .Returns(MockEfResult(Enumerators.SomeCollection).Object);

                return mockSomeDbEntities;
            }
        }

        private static Mock<TestableEfObjectResult<T>> MockEfResult<T>(Func<IEnumerator<T>> enumerator) where T : class 
        {
            var mock = new Mock<TestableEfObjectResult<T>>();
            mock.Setup(m => m.GetEnumerator()).Returns(enumerator);
            return mock;
        }
    }
}

The Enumerators class that I created for handing back the enumerator whenever the function is called on the mock simply looks like this. In this example I have the fake enumerator creating 5 rows of data:

using System;
using System.Collections.Generic;

namespace MyNamespace.FakeData
{
    public static class Enumerators
    {
        public static IEnumerator<Some_Result> SomeCollection()
        {
            yield return FakeSomeResult.Create(1);
            yield return FakeSomeResult.Create(2);
            yield return FakeSomeResult.Create(3);
            yield return FakeSomeResult.Create(4);
            yield return FakeSomeResult.Create(5);
        }
    }
}

And, as you can see, this simply relies on a class that creates each fake row of data:

namespace MyNamespace.FakeData
{
    public static class FakeSomeResult
    {
        public static Some_Result Create(int id)
        {
            return new Some_Result
            {
                Id = id,
            };
        }
    }
}

Being able to mock at this level really enables my ability to do BDD and only mock or fake the peripheries, never mocking or faking my code, so I get complete(r) test coverage.

Hope this helps those who, like me, were looking for a nice clean way to mock Entity Framework 6.

这篇关于用Moq模拟实体框架6 ObjectResult的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆