21

I'm trying to Setup the return of a call to an extension method and am receiving:

SetUp : System.NotSupportedException : Expression references a method that does not belong to the mocked object: m => m.Cache.GetOrStore<String>("CacheKey", () => "Foo", 900)

It seems to have a problem with referencing the GetOrStore method on the Cache object which is an extension method.

The code compiles but the test fails with this exception.

What do I need to do to setup the result of an extension method like this?

Jamie Dixon
  • 53,019
  • 19
  • 125
  • 162
  • 2
    Did you mock the methods/properties used in the extension method? – Ufuk Hacıoğulları May 23 '12 at 08:28
  • The extension method is a static method in a static class. I don't expect `GetOrStore` to actually be called and so mocking out the methods it calls shouldn't be necessary, right? – Jamie Dixon May 23 '12 at 08:30
  • `GetOrStore` calls `Get` and `Insert` on the `Cache` object. The `GetOrStore` method shouldn't actually be executing though since it's a mock... – Jamie Dixon May 23 '12 at 08:34
  • It shouldn't be necessary if extension method is never executed but if you are trying to mock it like an instance method, that won't work. – Ufuk Hacıoğulları May 23 '12 at 08:35
  • That's what I thought. I'm setting up the return from `GetOrStore` like: `_mockedHttpContextBase.Setup(m => m.Cache.GetOrStore("", () => "Bla", 900)).Returns(() => "Bla");` – Jamie Dixon May 23 '12 at 08:37
  • Nope that won't work, that method is not defined on your mocked type. – Ufuk Hacıoğulları May 23 '12 at 08:38
  • Makes sense. The problem I have now is that `Get` on `Cache` is not marked as `virtual` and so it can't be setup. (I get a `NotSupportedException`) – Jamie Dixon May 23 '12 at 08:42
  • You should be mocking abstractions instead of concrete classes. I don't know much about your design but you could try to get cache instance with constructor injection as a ICache or something. – Ufuk Hacıoğulları May 23 '12 at 08:47
  • Then I'll need to find a new way of mocking out the HttpContextBase. Thanks for all your help @UfukHacıoğulları. – Jamie Dixon May 23 '12 at 08:53

3 Answers3

32

Extension methods can not be mocked like instance methods because they are not defined on your mocked type. They are defined in other static classes. Since you can't simply mock those, you should mock all methods/properties used by the extension method.

This is an example of how extension methods tightly couples your code to other classes. Whatever you do, your class depends on those static methods. You can't mock it and test it in isolation. I suggest refactoring your code to move those methods to their classes of their own if there is any logic inside.

Ufuk Hacıoğulları
  • 37,978
  • 12
  • 114
  • 156
  • 1
    Thanks for your help. I decided to ditch the extension method and impliment my own ICache as sugested. Everything looks much simpler now. – Jamie Dixon May 23 '12 at 09:41
6

Moq cannot mock static methods, therefore you won't be able to mock your GetOrStore extension.

Instead just mock the Get and Insert methods of the Cache object.

Florian Greinacher
  • 14,478
  • 1
  • 35
  • 53
1

It is possible, although not pretty... I assume there is some internal cache object inside your extension method, or a reference to some cache somewhere. You can use reflection to replace the internal object to store the cache. You get something like this inside your test:

IFixture fixture = new Fixture().Customize(new AutoMoqCustomization());

Mock<ICache> internalCache = new Mock<ICache>();
internalCache.Setup(i => i.Get<String>("CacheKey")).Returns("Foo");

var cacheExtension = typeof(CacheExtensions);
var inst = cacheExtension.GetField("_internalCache", BindingFlags.NonPublic | BindingFlags.Static);
inst.SetValue(cacheExtension, internalCache.Object);

Note that this code is not tested, but it should explain the basic idea.

Sorskoot
  • 10,190
  • 6
  • 55
  • 98