开发者

Asserting a call to a public method on the same mock instance

开发者 https://www.devze.com 2023-03-27 19:14 出处:网络
I have the following test [Test] public void Attack_TargetWith3Damage_CausesAttackerToDeal3DamageToTarget()

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();
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号