Mocking Entity Framework 6 ObjectResult with Moq

不打扰是莪最后的温柔 提交于 2020-01-04 18:14:40

问题


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


回答1:


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.




回答2:


I have run into this problem many times

My solution is to mock a ObjectResult's GetEnumeartor method. This can easily be done within a test method will very little overhead

for example

say we have a repo method that calls a DBContext method that returns a ObjectResult<string>. The repo method would get the resulting value string by enumearting the ObjectResult<string> with maybe objResult.FirstOrDefault(). As you know the LINQ method just calls into the ObjectResult's enumeartor. Therefore, mocking the GetEnumeartor() function of the ObjectResult will do the trick. Any enumeartion of teh result will return our mocked enumerator.

Example


Here is a simple a simple func that produces an IEnumerator<string> from a string. This can be inlined with an anonymous method but it makes it harder to read for illustration purposes here.

var f = new Func< string,IEnumerator< string> >( s => ( ( IEnumerable< string > )new []{ s } ).GetEnumerator( ) );

this func can be called by passing a string like this

var myObjResultReturnEnum = f( "some result" );

this may make it easier for us to get the IEnumerator<string> we expect the ObjectResult<string>'s GetEnumerator() method to return so any repo method that calls the ObjectResult will receive our string

// arrange
const string shouldBe = "Hello World!";
var objectResultMock = new Mock<ObjectResult<string>>();
objectResultMock.Setup( or => or.GetEnumerator() ).Returns(() => myObjResultReturnEnum );

No we have a mocked ObjectResult<string> that we can pass into a repo method inferring our method will eventually call into the enumerator in some way and will get our shouldBe value.

var contextMock = new Mock<SampleContext>( );
contextMock.Setup( c => c.MockCall( ) ).Returns( ( ) => objectResultMock.Object );

// act
var repo = new SampleRepository( contextMock.Object );
var result = repo.SampleSomeCall( );

// assert
Assert.IsNotNull(result);
Assert.AreEqual(shouldBe, result);


来源:https://stackoverflow.com/questions/32277290/mocking-entity-framework-6-objectresult-with-moq

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!