Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return a value based on other method input

I'm trying to mock a very simple (simplified) interface which basically is an interface for a Cache service:

public interface ICache
{
  T Get<T>(String key);
  T Set<T>(String key, T value); // return T on success or null on fail
}

I want to mock this using Moq the following way:

  • If I call Get with a key that has not been set yet, it should return null
  • If I call Set with a key 'x' and value 'y', and then call Get with key 'x', it should return 'y'
  • If I overwrite key 'x' with value 'z', and then call Get with key 'x', it should return the new value 'z' and not 'y'.

I know I could create a fake object that implements the interface and which uses a Dictionary, but I was wondering if this can be done easily using Moq. Thanks!

like image 816
Razzie Avatar asked Sep 01 '25 16:09

Razzie


2 Answers

To me, the behavior you are describing implies an underlying dictionary. But for a simple unit test, I would probably just call Setup twice to manage those expectations.

mockCache.Setup(c => c.Get(It.Is<string>(x))).Returns(y);

//...other code
mockCache.Object.Set(x, z);
mockCache.Setup(c => c.Get(It.Is<string>(x)).Returns(z);
//...other code
like image 96
Steve Danner Avatar answered Sep 04 '25 05:09

Steve Danner


You can do that with callbacks that access a dictionary:

var dictionary = new Dictionary<string, object>();

var mock = new Mock<ICache>(MockBehavior.Strict);

mock.Setup(c => c.Set(It.IsAny<string>(), It.IsAny<object>()))
    .Callback((string k, object v) => dictionary[k] = v)
    .Returns((string k, object v) => v);

mock.Setup(c => c.Get<object>(It.IsAny<string>()))
    .Returns((string k) => dictionary.ContainsKey(k) ? dictionary[k] : null);

const string fooString = "foo";
const string barString = "bar";

mock.Object.Set(fooString, (object)barString);
var bar = mock.Object.Get<object>(fooString);

Assert.AreEqual(barString, bar);

But that isn't reusable as you'll have to copypaste it for each test that does something like this.

A fake in which you encapsulate this behavior seems to be the way to go.

like image 41
CodeCaster Avatar answered Sep 04 '25 06:09

CodeCaster