如何使用假存储库测试对象是否更新

时间:2010-09-23 08:29:16

标签: unit-testing tdd repository persistence

说我有以下业务逻辑:

    foreach (var item in repository.GetAll())
    {
        if (SomeConditition)
        {
            item.Status = Status.Completed;
            repository.Update(item);
        }
    }

现在我写下面的单元测试:

    public void Test()
    {
        var repository = new FakeRepository();
        repository.Add(new Item());

        RunBusinessLogic(repository);

        Assert.AreEqual(Status.Completed, repository[0].Status);
    }

FakeRepository在List上实现,GetAll()只返回列表。

虽然我可以使用这种方法测试大多数涉及的逻辑,但我无法验证我是否记得在业务代码中调用repository.Update()。由于FakeRepository.GetAll()返回实际对象,因此在调用Update()之前,业务代码已经修改了存储库对象。实际上,FakeRepository.Update()什么都不做。

我意识到我可以使用FakeRepository.Update来记录它已被调用,并在测试中断言。但是,如果我忘记调用Update,我也不能信任忘记更新,对吗?如果省略Update调用,我宁愿测试失败。

有什么想法吗?

5 个答案:

答案 0 :(得分:3)

模拟框架在这里可能很有用,因为它允许您验证实际调用哪些方法。仅举几例:

这三个流行的框架之间是nice comparison

答案 1 :(得分:1)

  

我意识到我可以使用   FakeRepository.Update来记录它   它被称为,一种断言   考试。但如果我忘记打电话   更新,我不能信任   记得断言更新,   对?我更喜欢测试   如果更新调用是,则会失败   删去。

当您注意到您没有测试调用Update时,或者您注意到未调用Update时,那就是您编写一个调用Update的测试时。并不是说存储库中的项目已完成,而是调用了Update(这是一个不同的测试)。您已经决定调用Update非常重要,因此您需要对其进行测试。实现它的方法正如您所概述的那样 - 在FakeRepository中记录要更新的调用,并对其进行测试。

答案 2 :(得分:1)

有几件事可以单独进行单元测试:Repository.getAll()Repository.update()方法,以及每个项目或每种项目的完成条件评估。我不确定我会在这里使用FakeRepository。

根据建议,另一种选择是让FakeRepository.update()方法记录和/或计算更新次数。

  

但是如果我忘记调用Update,我也不能信任忘记更新,对吗?

正确,但是当你遵循TDD过程时,你将首先失败你的测试 - 红色 - 然后你编写代码让它通过 - 绿色。您将首先编写断言,导致测试失败,然后调用update()方法,使测试通过。

答案 3 :(得分:0)

鉴于尚未接受答案,并且答案是正确的(以我的拙见!),我可能会建议Tor Hovland可能会问一个微妙的不同问题:

具体来说,Tor可能会询问如何确保他的单元测试调用Update调用,并且在这些情况下单元测试应该失败。

如果是这种情况,有两件事需要说明:

首先,如果这很重要,请考虑在单元测试中将测试代码添加到TearDown()或适当的方法,但是这样做的问题在于它实际上没有测试应用程序代码是否正确 - 可能存在无法调用Update调用的应用程序代码。

如果有应用程序代码没有正确调用Update(),并且代码恰好依赖于它(谁知道为什么......也许代码有一个错误,恰好可以工作,因为它没有'调用Update?),并且开发人员修改库以便更改某些行为以便自动调用Update(),或者现在调用有问题的代码调用它,现在您的单元测试会执行功能更改不测试,因为他们总是调用更新。

因此,这导致需要说明的第二点 - 单元测试需要测试应用程序代码的作用。如果单元测试“不使用严格正确的设计意义上的代码”并不重要,重要的是单元测试测试代码本身的结果。换句话说,测试代码调用Update(),或者测试Update()的副作用是否存在。

要重新迭代,并重新回答我有点啰嗦的答案,不要测试测试

答案 4 :(得分:0)

更好的方法是使用Dev Magic Fake,这样你就可以模拟数据库并且也可以是永久性的,你也可以模拟UI

只需添加对DevMagicFake.dll的引用

您可以编写以下代码:

[HttpPost]
public ActionResult Create(VendorForm vendorForm)
{
   var repoistory = new FakeRepository<VendorForm>();
   repoistory.Save(vendorForm);
   return View("Page", repoistory.GetAll());
}

这会将VendorForm永久保存在内存中,您可以随时检索它。您还可以为此对象或模型中的任何其他对象生成数据,有关Dev Magic Fake的更多信息,请参阅CodePlex上的以下链接:< / p>

http://devmagicfake.codeplex.com

由于

M.Radwan