Reputation: 66733
I have a test which looks a bit like this (I'm simplifying a bit from my real issue):
[Test]
public void Eat_calls_consumption_tracker_OnConsume()
{
var consumptionTrackerStub =
MockRepository.GenerateStub<IConsumptionTracker>();
var monkey = new Monkey(consumptionTrackerStub);
var banana = new Banana();
monkey.Eat(banana);
consumptionTrackerStub.AssertWasCalled(x => x.OnConsume(banana));
}
This would work fine, except that Monkey
disposes Banana
after eating it. Therefore, the banana object is no longer in a usable state. In particular, the Banana.Equals
implementation cannot work after Dispose
is called because it uses unmanaged resources that have been released.
Unfortunately AssertWasCalled
will cause Banana.Equals
to be called, which blows up, causing the test to fail. What is the best way to fix this?
Upvotes: 0
Views: 428
Reputation: 66733
Turns out you can force Rhino Mocks to check the arguments with object.ReferenceEquals
like this:
[Test]
public void Eat_calls_consumption_tracker_OnConsume()
{
var consumptionTrackerStub =
MockRepository.GenerateStub<IConsumptionTracker>();
var monkey = new Monkey(consumptionTrackerStub);
var banana = new Banana();
monkey.Eat(banana);
consumptionTrackerStub.AssertWasCalled(
x => x.OnConsume(
Arg<Banana>.Matches(
y => object.ReferenceEquals(y, banana))));
}
object.ReferenceEquals
still works even if the banana has been disposed, thus fixing the problem.
Upvotes: 0
Reputation: 19004
Several possible solutions:
BTW: now that I think about it, I think your code has a conceptual problem: how do you know some implementation of OnConsume will not want to keep the banana peel?
Upvotes: 0
Reputation: 6440
I would be inclined to suggest that the class that instantiates an object should dispose of it. It's never a good idea to pass a Banana to a Monkey and then trust it to Dispose properly after consuming.
Can you not pass the data required to instantiate Banana into Monkey, rather than the Banana itself? Alternatively, can you not Dispose of it in your calling class?
Upvotes: 2
Reputation: 25523
What if Monkey.Eat had a parameter of IBanana (or IFood), and IBanana descended from IDisposable? Then you could mock IBanana and even verify that Dispose was called on it.
EDIT: You can do it with Moq:
[Test]
public void Eat_calls_consumption_tracker_OnConsume()
{
var consumptionTrackerStub = new Mock<IConsumptionTracker>();
var monkey = new Monkey(consumptionTrackerStub.Object);
var banana = new Banana();
monkey.Eat(banana);
consumptionTrackerStub.Verify(x => x.OnConsume(It.IsAny<Banana>()));
}
public class Banana
{
public override bool Equals(object obj)
{
throw new ObjectDisposedException("Banana");
}
}
public class Monkey
{
public Monkey(IConsumptionTracker tracker)
{
_tracker = tracker;
}
public void Eat(object obj)
{
_tracker.OnConsume(obj);
}
private readonly IConsumptionTracker _tracker;
}
public interface IConsumptionTracker
{
void OnConsume(object obj);
}
Upvotes: 1