【问题标题】:SpecFlow Integration Testing with Database Patterns使用数据库模式进行 SpecFlow 集成测试
【发布时间】:2013-06-07 11:32:51
【问题描述】:

我正在尝试设置 SpecFlow 以进行集成/验收测试。我们的产品在 Sqlite 中有一个后备数据库(虽然不是很大)。

这实际上被证明是一个有点棘手的问题;如何为测试建模数据库?

我想知道其他人使用哪些模式对支持数据库进行集成/验收测试。

我可以想到以下几种方法:

  1. 将数据库编译到包含测试的程序集中,然后对每个测试进行卷影复制。不过看起来很慢。
  2. 我可以在内存中创建数据库并使用预先确定的数据填充它。
  3. 我可以在内存中创建数据库,并以某种方式让 Givens 填充数据库。这似乎会使测试变得非常臃肿,但可能会给它们更多的控制权并使测试不那么脆弱。
  4. 我可以抽象每个数据库交互并使用模拟。不喜欢这个想法,因为我也想用它来测试数据库交互。
  5. 将数据库编译到测试中并依靠清理代码将其返回到基本状态(这对我来说似乎很狡猾)。不想对事务执行此操作,因为某些测试会进行多次交互(因此编写一个项目,然后尝试以不同的权限读回它)。

【问题讨论】:

    标签: database integration-testing specflow acceptance-testing


    【解决方案1】:

    在考虑如何进行测试之前,我认为您可能会发现查看您想要测试的什么很有价值。

    what data 开始,我发现获取单个元素或少量元素并想象围绕它们的一组事件以便为您提供正确的测试数据确实很有帮助运行你的测试。例如;

    • 如果您正在研究医疗保健系统,您可能会定义一个人“Bob”,然后生成他的生活事件。 Bob 出生于 37 年前的今天,小时候从自行车上摔下来摔断了手臂,他结婚了,并育有两个孩子。
    • 如果您正在研究金融交易系统,您可能会查看几只股票的开盘和收盘之间的一天,例如“MSFT”和“APPL”。在这一天,您可能会看到一个开始低并攀登,另一个开始高并下降。一条消息传出,扭转了他们的命运。

    现在,您可以实际评估哪些方案真正适用于您的数据,例如“MSFT”和“APPL”全天可能有 1,000 次价格变化,因此生成 Givens 和 Mocks 将非常耗时。这些数据有助于预先捕获。另一方面,“Bob”数据在使用生成的数据时效果特别好,因为数据总是会发生变化,因此今天是他的生日。

    您的问题似乎不需要考虑的一件事是更新您的数据。例如,您可能希望有一组在实体生命周期的不同阶段工作的测试,例如一些测试处理“Baby Bob”,其他处理“10yr old Bob”,或“Married Bob”等。如果您的数据库是只读的,那么如果您可以编写测试,那么这不是问题,这样他们就不会查看其他数据,但有时您想通过测试构建一个故事。如果您的测试确实更改了数据,那么您将无法确保您的测试按顺序运行(请参阅MSTest OrderedTest 或 mbUnit DependsOn),或者您可以分离您的测试,以便它们各自处理一个独立的数据实体(这如果您的实体可以在一行中描述,那很好,但是当您必须阅读许多表才能获得它时会变得更难)。

    您可能还想考虑您正在测试什么代码,您可以在不同的测试集中改变方法。我目前正在开发一个具有 UI 视图、视图模型、客户端模型、多个通信系统和服务器模型的多层应用程序。我也有不同的测试集。我有一些在单层中工作的测试,模拟其他层以保持我的测试很小。其他测试启动本地服务器和本地客户端,并直接将两者连接起来。最后,我进行了一些测试,它们启动了一个完整的服务器进程,通过 EMS 进行通信,并使用除了 UI 视图之外的所有内容运行一些简单的客户端操作。

    所以现在来回答你的问题,

    1. 影子复制你的数据库 - 是的,我用 SQLServer Developer 做过一次,并且在运行测试之前复制了一个 xxx.mdb。然而,一些现代测试框架将并行运行测试,例如NCrunch,所以这会中断。
    2. 创建数据库并预填充 - 这个没有完成,但我担心的是测试将数据库更改为意外状态时会发生什么。其他测试在没有做错任何事情时都会失败。
    3. 创建数据库并使用 Givens - 我已经通过 [SetupFixture]在 Linq-to-sql 数据库上使用 NUnit 完成了这项工作。您仍然担心并行测试运行,您必须平衡你给定的粒度(参见StackOverflow-When do BDD scenarios become too specific),并且你有数据更新排序/数据隔离问题,但这可以很好地让你处理你的数据故事并在整个测试中增加数据。另一方面,如果一个测试失败并使数据处于不良状态,您最终可能会出现很多失败,但至少您只需要查看首先失败的那个。这种测试对于在他们的工作站上的开发人员来说也不会很好地发挥作用,因为他们不能只运行单个测试,特别是使用 NCrunch 等工具,它可以只运行代码已更改的测试。
    4. 模拟数据库 这就是我现在选择做事的方式。诀窍是,如果您个人遵循相当严格的 TDD 流程,只测试您正在使用的方法,那么您实际上最终会得到一些测试数据库交互的层,例如[Test]DALLayerTests.ShouldReadARowAndCreatePOCO(),但大多数其他人使用模拟数据来测试实际发生的情况,例如[Test]BusinessObjectPersonTests.ShouldGetBirthdayCongratulations()
    5. 使用清理代码 - 从未尝试过,听起来很狡猾 :-)

    【讨论】:

    • Re:第 4 点:根据定义,集成测试不是单元测试。单元测试独立于系统的其余部分(因此称为“单元”)对对象的行为(或状态)进行测试。另一方面,集成测试旨在测试单元之间的数据流和由此产生的业务流程(逻辑)——一起测试它们(因此,术语“集成”)。因此,在进行集成测试时访问数据库是完全合理的。另外,不要模拟你不拥有的东西——所以不要模拟数据库。
    • 实际上,不要嘲笑你不拥有的东西!编写测试是关于验证您控制的行为。如果您不能信任您的数据库按要求工作,那么您将向供应商提出错误报告,而不是修复代码。但是,如果它不是数据库,而是您组织中另一个团队的产品怎么办?您是否希望您的测试因为代码错误而失败?实际上你可能会,但你需要一些测试来突出你的内部代码正在工作,并且只有在连接到他们的系统时才会失败。
    • 你说在进行集成测试时访问数据库是合理的,我也同意你的看法,但是这个问题的上下文是在给定场景的情况下有哪些替代方案和陷阱。跨度>
    • 实际上,我在周日看到了这个很棒的视频,叫做Integration Tests are a Scam--名字很挑衅,但是,请听那个家伙的声音--他在中间设置了上下文--并且一旦你看到上下文,哇,他是对的!如果我们正在编写正确类型的单元测试并按照 SOLID 原则正确分割我们的代码,则确实不需要传统的开发人员集成测试。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-26
    • 1970-01-01
    • 2019-09-06
    • 2022-01-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多