DEV Community

Rajmond Burgaj
Rajmond Burgaj

Posted on

How to setup methods which return a built-in type and implement IEnumerable interface in Moq ?

Recently I was trying to make some unit testing and at some point I realized something was going wrong with my design but at the same time could not change it. So, the idea is I am using entity framework and executing stored procedures using this framework. All these procedures were encapsulated inside a service called IProcedureService.So, a normal method would look like this:

DbRawSqlQuery<UserVesselPermissionsResult> GetUserVesselPermissions(Guid userId, DateTime date);

The DbRawSqlQuery is an class from entity framework which I can not modify. The question for me was, how I am going to make this Moq setup code work:

_procedureService.Setup(x => x.GetUserVesselPermissions(It.IsAny<Guid>(), It.IsAny<DateTime>()))
        .Returns((DbRawSqlQuery<UserVesselPermissionsResult>) /*...what*/);

The real problem was due to class itself having only one internal constructor which could not be instantiated in Moq. That's because Moq can not instantiate classes only with internal constructor. Further more this class could have constructors with some parameters which I can not supply at this point(an example could be a DbConnection or something similar). In this case I tried to decompile the source and I could see another class named DbSqlQuery which was inheriting from DbRawSqlQuery and provided a normal constructor besides the internal one. At this point, I found an idea how to make this work. The idea was to create a new class and inherit from DbSqlQuery. My class now would look like this:

public class TestDbSqlQuery<T> : DbSqlQuery<T> where T : class
{
    private readonly List<T> _innerList; 

    public TestDbSqlQuery(List<T> innerList)
    {
        _innerList = innerList;
    }

    public override IEnumerator<T> GetEnumerator()
    {
        return _innerList.GetEnumerator();
    }
}

As you can from the code now I have achieved two things:

  1. I do have a class inheriting from DbRawSqlQuery which is what I need.
  2. Secondly I do have a class which can be instantiated and furthermore does accept a parameter in constructor which then can be tricked internally.

By tricked internally I mean this: - Since my methods all returned DbRawSqlQuery which at the end is just an IEnumerable type, then I thought all of these will be executing GetEnumerator() method to get the result. At this point I have provided a list as a parameter in the constructor from where I can easily return all the elements by overriding the GetEnumerator() method as I did above. I have a List<> as a parameter but this can be easily switched accordingly to IEnumerable or whatever type that supports GetEnumerator() method.

Now my test method setup would look like this:

_procedureService.Setup(x => x.GetUserVesselPermissions(It.IsAny<Guid>(), It.IsAny<DateTime>()))
.Returns(new TestDbSqlQuery<UserVesselPermissionsResult>(new List<UserVesselPermissionsResult>
{
    new UserVesselPermissionsResult
    {
        PermissionId = 1
    }
}));

and I will get the expected result at the end.

Hopefully this idea will help somebody struggling with setting up the methods which do return IEnumerable types.

Top comments (2)

Collapse
 
justinjstark profile image
Justin J Stark

Why are you returning a DbRawSqlQuery rather than an IEnumerable? Maybe you have a reason but you can avoid this mocking problem if you change your method signature to

IEnumerable<UserVesselPermissionsResult> GetUserVesselPermissions(Guid userId, DateTime date);

This also means consumers don't take a dependence on System.Data.Entity and your method technology can freely change with no change to consumers.

Another indication that something isn't right is you are writing unit tests for a method that returns an ORM-specific type. This leads me to ask the questions: Is your unit test useful? Could it be more useful if you abstract away the DbRawSqlQuery? Would you be better off writing your unit test at a higher level of the application?

Collapse
 
rajmondburgaj profile image
Rajmond Burgaj • Edited

Hi Justin, sorry for the late response. In my case I would have changed it to be IEnumrable but did not want to do so because I had to change many things in the services which I did not want for the moment. Maybe a later a refactoring task might fix this. In reality there might be a reason why you don't/cant change to IEnumerable because you might want to use some extra functionalities provided by the class(in this case DbRawSqlQuery) without having to cast first. But I agree with the fact it is better where possible to keep as less dependencies as possible.