Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 868
display name Avatar asked Dec 04 '14 04:12

display name


3 Answers

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.

like image 101
Sergey Kolodiy Avatar answered Nov 04 '22 02:11

Sergey Kolodiy


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.

like image 8
Jeppe Stig Nielsen Avatar answered Nov 04 '22 01:11

Jeppe Stig Nielsen


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();
like image 3
Mohamed Awadallah Avatar answered Nov 04 '22 01:11

Mohamed Awadallah