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

时间:2011-08-14 04:12:04

标签: c# unit-testing mocking nunit fakeiteasy

我有以下测试

[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 realAttacker不是wrappedAttacker攻击者因此没有拦截方法调用。

我该如何测试这个断言?可以用FakeItEasy完成吗?是否有一个不同的模拟框架允许我测试它?

2 个答案:

答案 0 :(得分: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());

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

在Moq中进行此验证的原因是DealDamage函数已标记为virtual。对于你的情况,这可能是一个交易破坏者,但它确实解决了“是否有一个不同的模拟框架允许我测试这个?”的问题。

答案 1 :(得分: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();
}