在 NUnit 测试中重构大型方法

Refactoring large methods in NUnit tests

我如何管理大型测试。我正在测试一个网络应用程序,其中一个功能是下订单,用户必须在下订单之前填写几个表格。

我可以用 C# 编写一个 selenium 测试来测试下新订单的整个流程。但是那个测试宁愿变得相当大。

简化流程如下所示:

  1. Select 1 个或多个客户的订单
  2. Select 1 个或多个产品与所选客户相关联
  3. 添加一些关于订单的元数据,例如姓名、必须完成的人、日期、评论等

用户必须在一些子表单中搜索客户和产品。

现在我可以编写一个遍历整个主要流程的(大型)测试。但是该测试很容易导致方法超过 100 行。
而且我还想测试某些替代流程,这将导致一种方法很容易与正常流程方法相同 80%。

但是,我知道您不应该编写相互依赖的测试。这就是我的困境。我的代码看起来像这样

[test]
public void NormalFlow()
{
    //Execute the first two steps
    //Around 100 lines

    //Execute the third step normally
    //around 50 lines
}

[test]
public void AlternativeFlow()
{
    //Execute the first two steps
    //Around 100 lines

    //Execute the third step, but follow alternative flow
    //around 50 lines
}

有很多重复代码,但我不能只从第三步开始,所以我必须走完前两步。我不能将前两个步骤分开作为单独的测试,因为那样会使我的测试相互依赖。

我该怎么办?如何避免在不创建依赖测试的情况下复制我的所有代码?

Now I can write one (large) test that walks through the entire primary flow. But that test could easily result in a method with 100+ lines. And I also want to test certain alternative flows, which would result in a method that could easily be 80% the same as the normal flow method.

仅仅因为您正在编写一个执行多项操作的测试,并不意味着您必须将其全部放在一个方法中。重构您的测试代码以确保它具有适当的质量是开发过程的一个重要部分。

I know you shouldn't write tests that depend on each other.

虽然这是事实,但我认为您可能有点从字面上理解了。根据您对系统的描述,这将是两个 相互依赖 的测试的示例:

测试一 为客户 XXX 创建新订单。

测试二 将产品 YYY 添加到客户 XXX 的未结订单中。

测试二依赖于测试一,因为如果测试一没有执行/失败,测试二也会失败,原因可能并不明显。

这不同于彼此不依赖的两个相关测试。因此,上述的替代方案是:

测试一个为客户 XXX 创建一个新订单。

测试二为客户ZZZ创建一个新订单并将产品YYY添加到订单中。

每个测试用例都是自包含的,可以 运行 隔离。正如您所说,这主要是因为测试二执行的许多处理与测试一相同。这没问题,但这并不意味着测试二的所有代码都必须在一个方法中。如果您正在编写生产代码,您可能会查看您的代码,识别重复并将其重构为不同的方法或 类 可以重用的代码。如果这使您的测试代码更易于阅读,那么这绝对是正确的做法。

因此,根据您的示例代码,您可能会得到如下内容:

[Test]
public void NormalFlow()
{
    var sessionDetails = Logon(customerCredentials);

    var openOrder = CreateOrder(sessionDetails);

    AddProductToOrder(openOrder, productDetails);

    AddMetaDataToOrder(openOrder, metaData);
}

[Test]
public void AlternativeFlow()
{
    var sessionDetails = Logon(customerCredentials);

    var openOrder = CreateOrder(sessionDetails);

    AddProductToOrder(openOrder, productDetails);

    AddMetaDataToOrder(openOrder, alternateFlowMetaData);
}

shared/duplicate 代码被推送到共享方法中。仅仅因为代码是共享的,并不意味着测试是依赖的。

正如@Sriram Sakthivel 在评论中所说,如果在每次测试开始时(例如登录)都有相同的代码,您可以做的另一件事是将其放入标记为使用 Setup 属性。请记住,目标是让您的测试代码易于 write/understand 和维护。