I have the following test
[Test]
public void Attack_TargetWith3Damage_CausesAttackerToDeal3DamageToTarget()
{
var realAttacker = CreateCreature(damage: 3);
var wrappedAttacker = A.Fake<ICreature>(x => x.Wrapping(realAttacker));
var target = A.Fake<ICreature>();
wrappedAttacker.Attack(target);
A.CallTo(() => wrappedAttacker.DealDamage(target, 3)).MustHaveHappened();
开发者_JAVA技巧}
The problem is that the call to DealDamage
from the Attack
method isn't being registered, because inside the method, this
is realAttacker
not wrappedAttacker
attacker hence the method call isn't being intercepted.
How can I test this assertion? Can this be done with FakeItEasy? Does a different mocking framework allow me to test this?
You can get pretty close to what you are after using Moq for your mocking framework.
Take this as an example:
public interface ICreature { ... }
public class Creature : ICreature
{
...
public void Attack(ICreature creature)
{
DealDamage(creature, 3); // Hard-coded 3 to simplify example only
}
public virtual void DealDamage(ICreature target, int damage) { ... }
}
.... Test ....
var wrappedAttacker = new Mock<Creature>();
var mockTarget = new Mock<ICreature>();
wrappedAttacker.Object.Attack(mockTarget.Object);
wrappedAttacker.Verify(x => x.DealDamage(mockTarget.Object, 3), Times.Once());
In this case I'm "wrapping" the a Creature
instance in a mock for the attacker role and creating a ICreature
mock for the target role. I then call the Attack
method from the attacker; verifying that the same attacker's DealDamage
was called (with the correct target and 3 damage), exactly one time.
What makes this verification possible in Moq is that the DealDamage
function is marked virtual
. This may be a deal-breaker for your situation, but it does address the "Does a different mocking framework allow me to test this?" question.
Thanks to @ckittel for pointing me to this answer. For this to work, the Creature
class needs to have a parameterless constructor, and the methods need to be virtual.
The one extra thing with FakeItEasy seems that you have to tell it to call base method, otherwise conceptually it's the same, just different syntax.
[Test]
public void Attack_TargetWith3Damage_CausesAttackerToDeal3DamageToTarget()
{
var attacker = A.Fake<Creature>();
A.CallTo(attacker).CallsBaseMethod(); //Otherwise it seems all calls are no-ops.
attacker.Stats.Damage = 3;
var target = A.Fake<ICreature>();
attacker.Attack(target);
A.CallTo(() => attacker.DealDamage(target, 3)).MustHaveHappened();
}
精彩评论