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:
- I do have a class inheriting from
DbRawSqlQuery
which is what I need. - 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)
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?
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 toIEnumerable
because you might want to use some extra functionalities provided by the class(in this caseDbRawSqlQuery
) without having to cast first. But I agree with the fact it is better where possible to keep as less dependencies as possible.