Question

I have a class, a service, and two interfaces:

public class MyBasicObject
{
    public MyBasicObject() { }

    public int Id { get; set; }        
    public string Name { get; set; }
}

public interface ICacheProvider
{
    T Get<T>(string key, Func<T> fetcher) where T:class;
}

public interface IMyBasicObjectRepository
{
    MyBasicObject GetByName(string name);
}

public class MyBasicObjectService
{
    public MyBasicObjectService(ICacheProvider cacheProvider, 
                                IMyBasicObjectRepository repository)
    {
        CacheProvider = cacheProvider;
        MyBasicObjectRepository = repository;
    }

    public ICacheProvider CacheProvider { get; set; }
    public IMyBasicObjectRepository MyBasicObjectRepository { get; set; }

    public MyBasicObject GetByName(string name)
    {
        return CacheProvider.Get<MyBasicObject>(name, () =>
                                MyBasicObjectRepository.GetByName(name));
    }
}

Using RhinoMocks, I'd like to be able to verify that when MyBasicObjectService.GetByName("AnUniqueName") gets executed, so does CacheProvider.Get("AnUniqueName", () => MyBasicObjectRepository.GetByName("AnUniqueName")). I have a fixture set up like so:

[TestFixture]
    public class MyBasicObjectServiceFixture
    {
        [Test]
        public void GetByNameShouldCallCacheProviderFunction()
        {
            // Arrange
            MockRepository mock = new MockRepository();
            IMyBasicObjectRepository repo = mock.DynamicMock<IMyBasicObjectRepository>();
            ICacheProvider cacheProvider = mock.DynamicMock<ICacheProvider>();
            MyBasicObjectService service = new MyBasicObjectService(cacheProvider, repo);

            cacheProvider.Expect(p => p.Get<MyBasicObject>("AnUniqueName", () => repo.GetByName("AnUniqueName")));

            mock.ReplayAll();

            // Act
            var result = service.GetByName("AnUniqueName");

            // Assert
            mock.VerifyAll();
        }
    }

I would expect this test to pass, but when run, the assertion fails, notifying me that the function laid out in cacheProvider.Expect is not being called. Am I missing something reg. mocking out and testing methods that take parameters of Func<>?

Edit:

So if I do:

cacheProvider.Expect(p => p.Get<MyBasicObject>("AnUniqueName", () => repo.GetByName("AnUniqueName"))).IgnoreArguments();

(that is to say, add the IgnoreArguments() method onto the end of the expect call)

...the test passes just fine. I'm assuming, then, it's a problem with the argument passed in. Is there something I'm doing wrong in the expect where the cache provider method is getting called but it chokes on the anonymous method getting passed in?

Was it helpful?

Solution

The problem is that the two anonymous methods (the one in the Expect and the one which is created in GetByName are two different objects and therefor are not equal. You can fix that by partially matching the arguments like this:

cacheProvider.Expect(p => p.Get<MyBasicObject>(Arg<string>.Is.Equal("AnUniqueName"), Arg <Func<MyBasicObject>>.Is.NotNull));

OTHER TIPS

What I ended up doing for the test was:

[TestFixture]
public class MyBasicObjectServiceFixture
{
    [Test]
    public void GetByNameShouldCallCacheProviderFunction()
    {
        // Arrange
        MockRepository mock = new MockRepository();
        IMyBasicObjectRepository repo = mock.DynamicMock<IMyBasicObjectRepository>();
        ICacheProvider cacheProvider = mock.DynamicMock<ICacheProvider>();
        MyBasicObjectService service = new MyBasicObjectService(cacheProvider, repo);

        cacheProvider.Expect(p => p.Get<MyBasicObject>(Arg<string>.Is.Equal("AnUniqueName"), Arg<Func<MyBasicObject>>.Is.NotNull))
                .WhenCalled(call => 
                    {
                        var repoCall = (Func<MyBasicObject>)call.Arguments[1];
                        repoCall.Invoke();
                    });
            repo.Expect(c => c.GetByName("AnUniqueName"));

        mock.ReplayAll();

        // Act
        var result = service.GetByName("AnUniqueName");

        // Assert
        mock.VerifyAll();
    }
}

This works specifically for my use case (invoking a database retrieval in case of a cache miss, and making sure the service uses the correct repository call at that time), but is sort of a not-so-great work-around if you're not planning on invoking the anonymous function right away. I'm sure there are other alternatives with .WhenCalled, but for right now, this is working for me.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top