5

我有以下测试

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

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

我如何测试这个断言?这可以用 FakeItEasy 完成吗?不同的模拟框架是否允许我对此进行测试?

4

2 回答 2

3

使用Moq作为模拟框架后,您可以非常接近您的实际情况。

以此为例:

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

在这种情况下,我将 aCreature 实例“包装”在攻击者角色的模拟中,并为目标角色创建ICreature模拟。Attack然后我从攻击者那里调用该方法;验证DealDamage是否调用了相同的攻击者(具有正确的目标和 3 点伤害),恰好是一次。

使这种验证在 Moq 中成为可能的原因是该DealDamage功能被标记virtual。对于您的情况,这可能会破坏交易,但它确实解决了“是否有不同的模拟框架允许我对此进行测试? ”问题。

于 2011-08-14T15:16:14.533 回答
1

感谢@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();
}
于 2011-08-14T16:59:57.187 回答