0

I have a Cache class as follows:

public class MyCache : ICacheProvider
{ 
    private readonly IMemoryCache _cache; 
    private readonly MemoryCacheOptions _options;
    private readonly ILogger<InMemoryCacheProvider> _logger;  

    public MyCache(IMemoryCache cache, MemoryCacheOptions options, ILogger<InMemoryCacheProvider> logger)
    {
      //Elided
    }

    public virtual void Set<T>(string key, T value, TimeSpan expiration) where T : class
    {  
        _cache.Set(key, value, expiration); 
    } 

    public virtual T Get<T>(string key) where T : class
    { 
        if (_cache.Get(key) is T result)
        { 
            return result; 
        }  
        return default(T);
    } 
    // removed some code for clarity
 }

ICacheProvider has methods like Set and Get.

Well how can I test this class? I need to test that set method actually sets something to the depencency. With FakeitEasy I have done the following:

    [Fact]
    public void SetTest()
    {
        var cache = A.Fake<MyCache>();
        var item = A.Fake<TestClass>();
        cache.Set("item", item);

        A.CallTo(() => cache.Set("item", item)).MustHaveHappened(); 
    }

But this didnt make too much sense to me.

What I am interested is, when I call the set method, I need to be able to check if there is really an object set in the fake cache or whatever. Same for Get and other methods.

Can you please elaborate?

DarthVader
  • 52,984
  • 76
  • 209
  • 300
  • when you run your lambda expression what happens? – Simon Price Jul 02 '18 at 15:56
  • That works. I understand that a call has happened. But i need to test the side effect. – DarthVader Jul 02 '18 at 15:57
  • I'm stuggling to see that the test is achieving, other than testing that the mock framework is working correctly. Why mock it rather than pass a `new MemoryCache("testCache")` to the `MyCache` constructor? – stuartd Jul 02 '18 at 15:58
  • That is why I am asking:) – DarthVader Jul 02 '18 at 16:01
  • Create an actual instance of the class under test and mock/ fake the dependencies to be injected. Invoke the methods under test and assert the desired behavior. Note however that `IMemoryCache` has a lot of extension methods and that you would need to mock the local interface members. Check https://stackoverflow.com/questions/42381018/mock-imemorycache-with-moq-throwing-exception/42387886#42387886 – Nkosi Jul 02 '18 at 16:04
  • @Nkosi one of the dependency is actually the MemoryCache that I use for storing items. My Class is a wrapper around it. – DarthVader Jul 02 '18 at 16:08
  • For the test you actually only need the interface/abstractions, not the implementation concerns as this is for an isolated unit test not an integration test. – Nkosi Jul 02 '18 at 16:09
  • @Nkosi what do you mean? I dont need to test the implementor class of the interface? I need to unit test that it works actually. – DarthVader Jul 02 '18 at 16:13
  • 2
    Mock the dependencies of `MyCache` and inject them into an actual instance of `MyCache`. You then call the members of `MyCache` and assert that it does what it is suppose to be doing. – Nkosi Jul 02 '18 at 16:17

1 Answers1

3

@Nkosi's comment is correct. A mocking framework is used by mocking the system under test's collaborators. Then the system under test can be exercised. Like this:

// mock a collaborator
var fakeCache = A.Fake<IMemoryCache>();

// Create a real system under test, using the fake collaborator.
// Depending on your circumstances, you might want real options and logger,
// or fake options and logger. For this example, it doesn't matter.
var myCacheProvider = new MyCache(fakeCache, optionsFromSomewhere, loggerFromSomewhere);

// exercise the system under test
myCacheProvider.Set("item", item, someExpriation);

// use the fake collaborator to verify that the system under test worked
// As @Nkosi points out, _cache.Set(key, value, expiration)
// from the question is an extension method, so you can't assert on it
// directly. Otherwise you could do
// A.CallTo(() => fakeCache.Set("item", item, expiration)).MustHaveHappened();
// Instead you'll need to assert on CreateEntry, which could be a little trickier
Blair Conrad
  • 233,004
  • 25
  • 132
  • 111
  • That is correct. The `Set` however in this case is an extension method. so it is not as simple as you would have expected. – Nkosi Jul 02 '18 at 16:34
  • Oh? I saw not evidence of that in the question. But yes, in that case, you'd want to assert against whatever method underlies the extension method. – Blair Conrad Jul 02 '18 at 16:37
  • I see your point. Check the link in my comment and you would see what I am referring to. – Nkosi Jul 02 '18 at 16:38
  • Ah. That comment was hidden for me. I was agreeing with another of your comments (same gist). Let's update the answer! – Blair Conrad Jul 02 '18 at 16:42