【问题标题】:Use FakeItEasy's A.CallTo() on another method in same object在同一对象的另一个方法上使用 FakeItEasy 的 A.CallTo()
【发布时间】:2013-07-29 13:56:14
【问题描述】:

使用 FakeItEasy,我如何检查我的对象的方法是否在同一个对象上调用了另一个方法?

测试:

[TestMethod]
public void EatBanana_CallsWillEat()
{
  var banana = new Banana();
  var myMonkey = new Monkey();

  myMonkey.EatBanana(banana);

  //this throws an ArgumentException, because myMonkey is a real instance, not a fake
  A.CallTo(() => myMonkey.WillEat(banana)
    .MustHaveHappened();
}

班级:

public class MyMonkey {
  private readonly IMonkeyRepo _monkeyRepo;

  public MyMonkey(IMonkeyRepo monkeyRepo) {
    _monkeyRepo = monkeyRepo;
  }

  public void EatBanana(Banana banana) {
    //make sure the monkey will eat the banana
    if (!this.WillEat(banana)) {
      return;
    }

    //do things here
  }

  public bool WillEat(Banana banana) {
    return !banana.IsRotten;
  }
}

我愿意接受建议。如果我说这一切都错了,请告诉我。

【问题讨论】:

  • 据我了解,这并不是 FIE 的真正用途。 FIE 提供了假对象,因此您的生产代码可以更容易地被戳和戳。正如您所指出的,您没有假货。根据我的经验,这种测试通常不是一个好主意。您的 MyMonkey 类应该是一个相当独立的单元,并且您最好在命令吃香蕉时测试它的整体行为,而不是担心它是否调用了自己的方法。比如,你能根据“// do things here”中的线索判断香蕉是否被吃掉了吗?
  • @BlairConrad 在我的真实场景中,WillEat 更复杂,并且有自己的测试,这只是 EatBanana 的测试之一。在这个测试中,如果我让 EatBanana 调用真正的 WillEat,我会不会在一次测试中测试两个功能?那么,如果 WillEat 发生变化,它可能会打破该测试,这是个坏消息,对吧?
  • 我明白你的意思。这很棘手。如果WillEat 存在于另一个对象上,我们都会敦促伪造该对象并将其注入MyMonkey(听起来很痛苦)。我只能说,我认为最好的默认位置是尝试从外部测试MyMonkey,依赖于客户的可观察结果。但是,您已经深思熟虑,并找到了一种解决方案,可以减轻您的痛苦并减少测试次数以及给定的测试次数。你最了解代码,所以如果它适合你……我只是想让你知道替代方案。

标签: c# unit-testing fakeiteasy


【解决方案1】:

你为什么要模拟测试对象? 究竟你想测试什么?对WillEat 的调用发生的验证价值不大。它向消费者提供什么信息?毕竟,消费者并不关心方法是如何实现的。消费者关心结果是什么

猴子吃了没烂的香蕉会怎样?您的测试应该回答这个问题:

[TestMethod]
public void EatBanana_CAUSES_WHAT_WhenBananaIsNotRotten()
{
    var repo = A.Fake<IMonkeyRepo>();
    var monkey = new Monkey(repo);
    var freshBanana = new Banana { IsRotten = false };

    monkey.EatBanana(freshBanana);

    // verifications here depend on what you expect from
    // monkey eating fresh banana
}

请注意,您可以对IMonkeyRepo 进行各种验证,这是在此处正确伪造和注入的。

【讨论】:

  • 好点。如果我不模拟 WillEat,并且允许 EatBanana 调用真正的 WillEat,那么我是否在一次测试中测试了两个功能?在这个例子中,WillEat 是微不足道的,但在我的实际情况下,它更复杂,并根据它的参数返回一个派生对象,所以我认为模拟它并验证它是否被调用是正确的方法。我目前对 WillEat 进行了单独的测试,对 EatBanana 进行了其他一些测试,包括验证是否调用了 repo。也许我太细了?
  • @JoshNoe: 或者你的粒度不够。如果WillEat 这么复杂,也许它应该存在于自己的类中?也许将这个特征提取到 food-evaluator-sort-of-thing 并将其注入猴子更有意义?测试应该简单——如果不是,通常是一个很好的指标,表明可以重新审视设计。顺便说一句,为什么Monkey 会依赖于IMonkeyRepository
【解决方案2】:

这是可以做到的。 如果WillEat 方法是虚拟的 - 否则 FakeItEasy 将无法伪造它。

通过该更改,您可以这样做:

[TestMethod]
public void EatBanana_CallsWillEat()
{
    var fakeMonkey = A.Fake<MyMonkey>();

    fakeMonkey.EatBanana(new Banana());

    A.CallTo(()=>fakeMonkey.WillEat(A<Banana>._)).MustHaveHappened();
}

我仍然不相信这是一个好主意(正如我在 cmets 中咆哮的那样) - 我认为你最好依赖其他可观察到的行为,但我不熟悉你的系统。如果您认为这是最好的方法,那么示例代码应该适合您。

【讨论】:

    猜你喜欢
    • 2013-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多