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;
names开发者_开发问答pace 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());
}
}
}
The point that everyone seems to be glossing over is that you do not need to stub in order to perform an "assert was called". And, the way you have it written, Arg<T>.Is.Anything
, it'll ignore the type. You're not verifying any generic constraints. You want to use Arg<T>.Is.TypeOf<T>
. Check the documentation for more details.
-----------------------------------------------
| Arg<T>.Is | |
===============================================
| Anything() | No constraints |
-----------------------------------------------
| TypeOf<T>() | Argument is of a certain type |
-----------------------------------------------
Your Fixed code snippet is still too complicated. You aren't using the data
object that you're saving in the "when called". And you don't need it to do a simple "assert was called".
@leebrandt's code looks as correct and simple as it gets, without introducing an automocking container.
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));
}
精彩评论