在同一模拟实例上断言对公共方法的调用

本文关键字:方法 调用 断言 模拟 实例 | 更新日期: 2023-09-27 18:05:24

我有以下测试

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

问题是Attack方法对DealDamage的调用没有被注册,因为在方法内部,thisrealAttacker而不是wrappedAttacker攻击者,因此方法调用没有被拦截。

如何测试这个断言?FakeItEasy能做到这一点吗?是否有不同的mock框架允许我对此进行测试?

在同一模拟实例上断言对公共方法的调用

在使用Moq作为mock框架后,您可以获得非常接近的效果。

以以下为例:

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());
在本例中,我将Creature 实例"包装"在攻击者角色的模拟中,并为目标角色创建ICreature模拟。然后我从攻击者那里调用Attack方法;验证同一个攻击者的DealDamage被调用(正确的目标和3点伤害),正好是一次。

在Moq中使此验证成为可能的原因是DealDamage函数被标记为virtual。对于您的情况,这可能是一个交易破坏者,但它确实解决了"不同的mock框架允许我测试这个吗?""问题。

感谢@ckittel为我指出这个答案。要做到这一点,Creature类需要有一个无参数的构造函数,并且方法需要是虚的。

FakeItEasy的一个额外的事情似乎是你必须告诉它调用基方法,否则在概念上它是相同的,只是语法不同。

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