Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF6 - Cannot Mock Return Value for ObjectResult<T> for Unit Test

I have code similar to this in a method that I'm trying to unit test:

return _context.usp_get_Some_Data(someStringParam).FirstOrDefault();

The stored proc call return type:

ObjectResult<usp_get_Some_Data_Result>. 

In my unit test, I'm trying to do something like this (using NUnit and Moq):

var procResult = new ObjectResult<usp_get_Some_Data_Result>();
mockContext.Setup(m => m.usp_get_Some_Data(It.IsAny<string>()))
    .Returns(procResult);

However, I'm not able to create an instance of ObjectResult (this is System.Data.Entity.Core.Objects.ObjectResult<T>, not the older System.Data.Objects one). It doesn't have a public parameterless constructor, but the documentation says it has a protected one. From my testing, he documentation appears to be incorrect.

What I've Tried: I've tried creating a derived class and calling base() on the constructor, and I've also tried using reflection (both Activator.CreateInstance and invoking the ConstructorInfo with BindingFlags of NonPublic, all of which have failed (it appears from my debugging this that the type does have three private constructors, all of which have 3 or more parameters, but unfortunately it looks like a major effort to figure out what is actually required for these parameters).

I've also tried creating an IEnumberable<usp_get_Some_Data_Result> and casting it to ObjectResult<usp_get_Some_Data_Result> but the cast fails. In addition, I've tried something like

var mockObjectResult = new Mock<ObjectResult<usp_get_Some_Data_Result>>();

Pretty much everything I've tried fails with a similar error about a default constructor not being available.

Question: Is there any way to create an instance of ObjectResult<T> for unit testing, or is there any other type that I can create that can be successfully cast to ObjectResult<T>?

like image 335
dgavian Avatar asked Jun 26 '15 18:06

dgavian


3 Answers

Maybe I'm missing something, but can't you just do this:

class TestableObjectResult<T> : ObjectResult<T>
{
}

And then in your test:

var mockObjectResult = new Mock<TestableObjectResult<usp_get_Some_Data_Result>>();

MockObject does have a protected constructor, you don't really have to do anything to call it, since it doesn't have any parameters, the auto-wiring will take care of it when you construct the testable version, so I'm not sure what you mean by you're "calling base() on the constructor"...

If I right click on ObjectResult and select goto definition the top of the file looks like this:

public class ObjectResult<T> : ObjectResult, IEnumerable<T>, IEnumerable, IDbAsyncEnumerable<T>, IDbAsyncEnumerable
{
    // Summary:
    //     This constructor is intended only for use when creating test doubles that
    //     will override members with mocked or faked behavior. Use of this constructor
    //     for other purposes may result in unexpected behavior including but not limited
    //     to throwing System.NullReferenceException.
    protected ObjectResult();
like image 187
forsvarir Avatar answered Nov 15 '22 08:11

forsvarir


As noted, I'm adding this answer to cover creating the Enumerator so the above can actually test some fake data:

Within the [TestFixture] class, create a method like the following:

private static IEnumerator<usp_get_Some_Data_Result> GetSomeDataResultEnumerator()
{
    yield return FakeSomeDataResult.Create(1, true);
    yield return FakeSomeDataResult.Create(2, false);
}

As provided in the previous answer, this handy little wrapper class allows instantiating the ObjectResult:

public class TestableObjectResult<T> : ObjectResult<T> { }

As provided in the previous answer, within the [SetUp] method:

var mockObjectResult = new Mock<TestableObjectResult<usp_get_Some_Data_Result>>();

Following this new Mock creation, set it up to return the Enumerator:

mockObjectResult.Setup(d => d.GetEnumerator()).Returns(GetSomeDataResultEnumerator());

Now the OP can return some fake data from the mockContext without it throwing a null reference exception when trying to get the enumerator:

mockContext.Setup(m => m.usp_get_Some_Data(It.IsAny<string>()))
    .Returns(mockObjectResult);

BTW, I'm just using a helper class to construct my fake data, to eliminate redundancy:

public static class FakeSomeDataResult
{
    public static usp_get_Some_Data_Result Create(int index)
    {
        return new usp_get_Some_Data_Result
        {
            SomeFriendlyNameProperty = string.Format("Some Data Result {0}", index),
        };
    } 
}
like image 33
Jim Speaker Avatar answered Nov 15 '22 09:11

Jim Speaker


As @forsvarir mentioned, you can create a TestableObjectResult class and override the GetEnumerator() to return whatever you want.

Something like this:

private class TestableObjectResult : ObjectResult<Animal>
    {
        public override IEnumerator<Animal> GetEnumerator()
        {
            return new List<Animal>() { new Animal(), new Animal() }.GetEnumerator();
        }
    }
like image 36
Gokulnath Avatar answered Nov 15 '22 09:11

Gokulnath