Expression references a method that does not belong to the mocked object
Asked Answered
A

4

45

I have an api service that calls another api service. When I set up the Mock objects, it failed with an error:

NotSupportedException: expression references a method that does not belong to the mocked object.

This is the code:

private Mock<IEnumerable<ICarrierApiService<AccountSearchModel>>> _mockCarrierService;
private Mock<IApiService<AccountSearchModel>> _mockApiService;

[SetUp]
public void SetUp()
{
  _mockApiService = new Mock<IApiService<AccountSearchModel>>();
  _mockCarrierService = new Mock<IEnumerable<ICarrierApiService<AccountSearchModel>>>();
  _mockApiService.Setup(x => x.GetFromApiWithQuery(It.IsAny<string>())).Returns(ApiValue());

  // Error occurred when call _mockApiService.GetFromApiWithQuery() in .Select()
  _mockCarrierService.Setup(x => x
            .Select(s => s
                .GetFromApiWithQuery(It.IsAny<string>())).ToList())
                .Returns(new List<IQueryable<AccountSearchModel>> { ApiValue() });
}

I read Expression testing with Moq but it didn't work for my case. If I remove this _mockCarrierService.Setup(), the test case can run but fails with a NullReferenceException because it didn't have a valid List<IQueryable<AccountSearchModel>> set up.

Any idea how I can achieve this?


Footnote: Current Solution

FWIW, here's the solution that I currently use. I am all ears for a better approach to the issue (until Moq starts supporting mocking extension methods).

private List<ICarrierApiService<AccountSearchModel>> _mockCarrierService;
private AccountSearchController _mockController;
private Mock<ICarrierApiService<AccountSearchModel>> _mockApiService;

[SetUp]
public void SetUp()
{
   _mockApiService = new Mock<ICarrierApiService<AccountSearchModel>>();
   _carrierServiceMocks = new List<ICarrierApiService<AccountSearchModel>> { _mockApiService.Object };
   _mockApiService.Setup(x => x.GetFromApiWithQuery(It.IsAny<string>())).Returns(ApiValue());
   _mockController = new AccountSearchController(_carrierServiceMocks);
}

Footnote: alternative mocking framework

I've also found a commercial mocking framework that supports mocking extension method and link to the how-to docs: Telerik JustMock.

Anaclinal answered 4/12, 2014 at 4:8 Comment(0)
D
78

This problem occurs because you are trying to mock Select method, which is an extension method, not an instance method of IEnumerable<T>.

Basically, there is no way to mock an extension method. Have a look at this question for some ideas that you may find useful.

UPD (12/11/2014):

To gain more understanding on mocking extension methods, think about the following:

  • Although extension methods are called as if they were instance methods on the extended type, they are actually just a static methods with a bit of syntactic sugar.

  • Extension methods from System.Linq namespace are implemented as pure functions — they are deterministic and they don't have any observable side effects. I agree that static methods are evil, except those that are pure functions — hope you would agree with this statement too :)

  • So, given an object of type T, how would you implement static pure function f(T obj)? It is only possible by combining other pure functions that are defined for object T (or any other pure functions, actually), or by reading immutable and deterministic global state (to keep function f deterministic and side-effect-free). Actually, "immutable and deterministic global state" has more convenient name — a constant.

So, it turns out that if you follow the rule that static methods should be pure functions (and it looks like Microsoft follows this rule, at least for the LINQ methods), mocking an extension method f(this T obj) should be reducible to mocking non-static methods or state used by that extension method — simply because that extension method relies on the obj instance methods and state in its implementation (and possibly on the other pure functions and/or constant values).

In case of IEnumerable<T>, Select() extension method is implemented in terms of foreach statement which, in turn, uses GetEnumerator() method. So you can mock GetEnumerator() and achieve required behavior for extension methods that rely on it.

Deictic answered 4/12, 2014 at 8:57 Comment(2)
Thanks for the help. I found another article from the same author of the one that you mentioned here. It might work if I add more complicate [Fact] method to it. I have to try to know for sure.Anaclinal
@IsabelHM, I updated the answer, so you might think about the problem from a different perspective :)Deictic
B
8

You have:

_mockCarrierService = new Mock<IEnumerable<ICarrierApiService<AccountSearchModel>>>();

So you mock IEnumerable<>. The only member IEnumerable<> has is a method GetEnumerator() (plus another method with the same signature GetEnumerator() inherited from the base interface). The Select method is really an extension method (as was pointed out in the first answer) which is a static method that works by calling GetEnumerator() (possibly through C# foreach statement).

It is possible to make things work by doing Setup of GetEnumerator on your mock.

However, it is much simpler to simply use a concrete, non-mock type which "is" IEnumerable<>, such as List<>. So try:

_mockCarrierService = new List<ICarrierApiService<AccountSearchModel>>();

Then add an entry to the List<>. What you should add, is a Mock<ICarrierApiService<AccountSearchModel>> on which GetFromApiWithQuery Method is setup.

Brierroot answered 4/12, 2014 at 12:20 Comment(1)
yes, that's what I realized after reading the article @Sergey mentioned in his answer. I have to try to know for sure. Thanks!Anaclinal
D
3

also if u need to Mock IConfiguration u can use this code below:

var builder = new ConfigurationBuilder()
        .AddInMemoryCollection(new Dictionary<string, string>
        {
            { "your-key", "your value" }
        });
        var config = builder.Build();
Degraw answered 15/8, 2019 at 17:3 Comment(0)
I
0

Since the question about mocking IConfiguration is referring this answer as duplicate, I will contribute my dime here. This is how I "Mock" IConfiguration, which is, in my opinion, a bit cleaner:

    private static IConfiguration GetConfigurationMock(string jsonConfiguration)
    {
        var byteArray = Encoding.UTF8.GetBytes(jsonConfiguration);
        var stream = new MemoryStream(byteArray);

        var conf = new ConfigurationBuilder();
        conf.AddJsonStream(stream);
        var confRoor = conf.Build();

        return confRoor; 
    } 

Original question: How to setup Mock of IConfigurationRoot to return value

Immeasurable answered 10/3, 2020 at 11:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.