Search code examples
c#rhino-mocksinvalidoperationexceptionmspecgeneric-method

How do I AssertWasCalled a generic method with three different types using RhinoMocks?


I'm trying to learn Rhino Mocks AAA syntax, and I'm having trouble asserting a certain method (with any argument value) was called. I'm using Machine.Specifications as my testing framework.

This particular method is generic and I want to make sure it was called three times with three different types.

repo.Save<T1>(anything), repo.Save<T2>(anything), and repo.Save<T3>(anything)

I stubbed the function for each type. But I'm getting an interesting result. (below)

[Subject("Test")]
public class When_something_happens_with_constraint
{
    static IRepository repo;
    static TestController controller;
    static ActionResult result;

    Establish context = () =>
    {
        repo = MockRepository.GenerateMock<IRepository>();
        controller = new TestController(repo);
        repo.Stub(o => o.Save<Something>(Arg<Something>.Is.Anything));
        repo.Stub(o => o.Save<SomethingElse>(Arg<SomethingElse>.Is.Anything));
        repo.Stub(o => o.Save<AnotherOne>(Arg<AnotherOne>.Is.Anything));
    };

    //post data to a controller
    Because of = () => { result = controller.SaveAction(new SomethingModel() { Name = "test", Description = "test" }); };

    //controller constructs its own something using the data posted, then saves it. I want to make sure three calls were made.  
    It Should_save_something = () => repo.AssertWasCalled(o => o.Save<Somethign>(Arg<Something>.Is.Anything));
    It Should_save_something_else = () => repo.AssertWasCalled(o => o.Save<SomethingElse>(Arg<SomethingElse>.Is.Anything));
    It Should_save_another_one = () => repo.AssertWasCalled(o => o.Save<AnotherOne>(Arg<AnotherOne>.Is.Anything));
}

The result is two exceptions and a pass.

The first call throws:

System.InvalidOperationException: No expectations were setup to be verified, ensure that the method call in the action is a virtual (C#) / overridable (VB.Net) method call

The second one throws:

System.InvalidOperationException: Use Arg ONLY within a mock method call while recording. 1 arguments expected, 2 have been defined.

The third one passes...for some odd reason.

I've also tried using GenerateMock() with Expect in my setup as well as using GenerateStub() with Stub. Both ended up with the exact same result. I've gotta be doing something wrong.

I'm using: MachineSpec 0.3.0.0 and RhinoMocks 3.6.0.0

Any ideas?

-----FIXED----------

Here's the full (working version) with Lee's help. I am using an extra (non-linq) layer. My actual problem was that one of my tests re-used the wrong lambda variable in the offline real code. It Should_do_something = () => repo.AssertWasCalled(o=>repo.Save(data)); //bad lambda

So here's a sample of the correct test for reference.

using System;
using System.Linq;
using System.Collections.Generic;
using Machine.Specifications;
using Rhino.Mocks;

namespace OnlineTesting.Specifications
{
    public interface Repository
    {
        void Save<T>(T data);
        IQueryable<T> All<T>();
    }

    public interface Service
    {
        void SaveItem(Item data);
        void SaveAnotherItem(AnotherItem data);
        void SaveOtherItem(OtherItem data);
        List<Item> GetItems();
        List<AnotherItem> GetAnotherItems();
        List<OtherItem> GetOtherItems();
    }

    public class ConcreteService : Service
    {
        Repository repo;
        public ConcreteService(Repository repo)
        {
            this.repo = repo;
        }
        public void SaveItem(Item data)
        {
            repo.Save(data);
        }
        public void SaveAnotherItem(AnotherItem data)
        {
            repo.Save(data);
        }
        public void SaveOtherItem(OtherItem data)
        {
            repo.Save(data);
        }

        public List<Item> GetItems()
        {
            return repo.All<Item>().ToList();
        }
        public List<AnotherItem> GetAnotherItems()
        {
            return repo.All<AnotherItem>().ToList();
        }
        public List<OtherItem> GetOtherItems()
        {
            return repo.All<OtherItem>().ToList();
        }
    }

    public class Item
    {
        public int Id { get; set; }
    }
    public class OtherItem
    {
    }
    public class AnotherItem
    {
    }


    public class When_something_else_happens
    {
        Establish context = () =>
        {
            _repository = MockRepository.GenerateMock<Repository>();
            _service = new ConcreteService(_repository);
            _controller = new TestController(_service);

            _repository.Stub(o => o.Save<Item>(Arg<Item>.Is.Anything)).WhenCalled(
                new Action<MethodInvocation>((o) =>
                {
                    var data = o.Arguments.FirstOrDefault() as Item;
                    if (data != null && data.Id == 0)
                        data.Id++;
                }));
        };

        Because of = () => _controller.DoSomethingElse();

        It should_save_the_first_thing = () =>
             _repository.AssertWasCalled(repo => repo.Save(Arg<Item>.Is.Anything));

        It should_save_the_other_thing = () =>
             _repository.AssertWasCalled(repo => repo.Save(Arg<OtherItem>.Is.Anything));

        It should_save_the_last_thing = () =>
             _repository.AssertWasCalled(repo => repo.Save(Arg<AnotherItem>.Is.Anything));

        static Repository _repository;
        static TestController _controller;
        static Service _service;
    }

    public class TestController
    {
        readonly Service _service;

        public TestController(Service service)
        {
            _service = service;
        }

        public void DoSomethingElse()
        {
            _service.SaveItem(new Item());
            _service.SaveOtherItem(new OtherItem());
            _service.SaveAnotherItem(new AnotherItem());
        }
    }
}

Solution

  • Give this a try.

    [Subject("Test")]
    public class When_something_happens_with_constraint
    {
        static IRepository repo;
        static TestController controller;
        static ActionResult result;
    
        Establish context = () =>
        {
            repo = MockRepository.GenerateMock<IRepository>();
            controller = new TestController(repo);
        };
    
        //post data to a controller
        Because of = () => result = controller.SaveAction(new SomethingModel() { Name = "test", Description = "test" });
    
        //controller constructs its own something using the data posted, then saves it. I want to make sure three calls were made.  
        It Should_save_something = () => repo.AssertWasCalled(o => o.Save(Arg<Something>.Is.Anything));
        It Should_save_something_else = () => repo.AssertWasCalled(o => o.Save(Arg<SomethingElse>.Is.Anything));
        It Should_save_another_one = () => repo.AssertWasCalled(o => o.Save(Arg<AnotherOne>.Is.Anything));
    }