简体   繁体   中英

Moq Setup does not work as expected in a separate class

I am using moq version 4.2 to unit test some controllers of web api 2 with EF as the ORM. Since a lot of tests need a mocked IEntityRepository<User> so I use a static method in a separate class called TestHelper to create such a fake repository so that I can just call this method in all the tests where needed. Here is the static method:

internal static IEntityRepository<User> GetSingleUserMockRepository(User user, bool noTracking = true, params Expression<Func<User, object>>[] properties)
{
            var userRepository = new Mock<IEntityRepository<User>>();
            if (properties.Any())
                userRepository.Setup(x => x.GetSingleInclude(user.Key, noTracking, properties)).Returns(Task.FromResult(user));
            else
                userRepository.Setup(x => x.GetSingle(user.Key, noTracking)).Returns(Task.FromResult(user));

            return userRepository.Object;
}

I am not showing other method details here because don't think they're relevant to my problem. The thing is when calling this static method in my tests and pass the returned user repository into controllers the test will fail because it cannot find the user ie Setup didn't work!. However, if I repeat the test implementation within my tests to create the mock repository as a local variable; everything works fine.

Namely, this doesn't work (where Devices is a navigation property on the User entity, again don't think the detail matters here) eg if I pass the returned userRepository into my controller the test will fail.:

var userRepository = TestHelper.GetSingleUserMockRepository(user, true, x=> x.Devices);

But this works eg if I just use a local variable in the test and pass the userRepository.Object into my controller everything works as expected:

var userRepository = new Mock<IEntityRepository<User>>();
userRepository.Setup(x => x.GetSingleInclude(user.Key, true, u => u.Devices)).Returns(Task.FromResult(user));

Can someone please explain why? I thought these two ways are equivalent but clearly not in practice.

This is maybe not a full answer (yet), but still to long to fit into a Stack Overflow comment.

When you do:

userRepository.Setup(x => x.GetSingleInclude(user.Key, noTracking, properties)).Returns(...);

you are saying: When GetSingleInclude is called with that user.Key and that noTracking and in particular that properties which is an array of expression trees, Moq shall return what you specify.

However, if Moq gets a call to GetSingleInclude where one or more of the arguments are not equal to the values you supplied, your setup is not relevant, and Moq returns instead the default value which is null . (If you had used MockBehavior.Strict instead, it would have thrown an exception instead of silently just returning null , which could be more helpful for understanding the issue.)

So when are two arrays of expression trees equal? If Moq uses the default equality comparer for arrays, they are only "equal" when they are the same array instance. If Moq uses some entry-wise comparison, it will come down to whether each expression tree is equal as reference (ie same Expression<> instance). In any case you may have trouble.

The question is why it works in the other case. Does GetSingleInclude have special overloads for when the number of properties (expression trees) is little?

What is the result of running the code from this question ?

I am saying that you may be facing the same problem as in the thread Mocking and verifying call to method containing an Expression<Func<T,bool>> parameter .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM