【问题标题】:Creating and running tests创建和运行测试
【发布时间】:2012-03-08 06:57:19
【问题描述】:

我使用单元测试向导在我的解决方案中创建了一个单元测试:我选择了类及其所有方法和属性,然后向导为每个类创建了一个包含测试方法的新文件测试方法。

显然,测试应该是可靠的,也就是说,只有当模块没有按预期工作而不是因为测试构建不佳时,测试才会失败。所以第一个问题是:构建测试应该遵循哪些准则?

在创建我的第一个测试时,我试图编写简单的测试用例,从而减少出错的可能性:例如,要测试一个集合,您需要向它添加一些元素,所以我创建了一个私有方法,例如在我的测试单元中关注:

// This is not a test method, but a support method for the test methods.
private void AddSomeElements(ICollection c, int count)
{
    Random rand = new Random();
    ...
    for(int i=0; i < count; i++)
        c.Add(...);
}

因此,例如,Count 属性的测试可能是:

/// <summary>
///A test for Count
///</summary>
[TestMethod()]
public void CountTest()
{
    HostsCache target = new HostsCache();
    AddSomeElements(target, 100);
    int actual;
    actual = target.Count;
    Assert.AreEqual<int>(100, actual);
 }

这种方法正确吗? 假设Add方法返回一个值(例如bool值):在这种情况下,上面的私有方法是否也应该返回这个值?

【问题讨论】:

    标签: c# .net visual-studio-2010 unit-testing


    【解决方案1】:

    规则 1:不要试图煮沸海洋。一次只测试一小段代码。对于单元测试,尽量减少外部依赖和交互。例如,这包括不花时间验证核心操作系统是否正常工作,以及 .NET 框架提供的核心类。测试您的代码是否与这些外部事物正确交互是件好事,但请记住,您的重点是测试您编写的代码,而不是测试您的代码使用的其他所有人的代码。

    确保所有部分都能很好地协同工作是集成测试的工作,这是与单元测试截然不同的测试方案。集成测试往往需要与单元测试不同的工具和不同的策略,最终与单元测试有不同的目标。

    如果您正在编写自己的标准 ICollection 接口实现,那么您应该对您的集合实现进行单元测试。否则,您无需测试在 .NET 提供的股票集合类上调用 ICollection.Add() 是否会执行应有的操作。

    如果您的类在内部使用一个集合并公开操作该集合内容的方法,则一种方法是仅使用您的类的公共方法来使该类更改其内部状态,并且仅使用公共方法来看看那个动作的效果。

    但是,在许多情况下,内部状态可能无法通过公共方法完全公开。你可以戳这个物体,但你不能真正看到里面到底发生了什么。抽象有利于系统设计,因为它对消费者隐藏了实现细节,以便将来可以根据需要更改细节,同时保持相同的公共接口和语义契约。隐藏内部细节有利于系统的弹性和寿命,但会给测试带来障碍。

    在这种情况下,启用类的某种钩子或接口以允许您的测试查看内部状态很有用。这确实使您的测试更紧密地绑定到实现细节(更改代码,您也必须更改测试),但它也使您的测试能够更详细地评估公共行为是否具有预期的效果对象的状态,尤其是当对象故意掩盖其内部状态时。

    这种内部访问挂钩的一个示例是 .NET 中的 InternalsVisibleTo 属性。您在生产程序集中声明您的测试程序集可以访问声明为“内部”可见性的类成员。您可能会自动声明为私有的东西可以被轻推到内部,以使其对您的单元测试可见。数据仍然受到普通客户端的保护。

    您应该查找的另一个术语是在测试中使用“模拟”类。模拟是一个假类,它提供了被测试代码所需的接口或类型的最小实现,用于将您有兴趣测试的代码与您现在不感兴趣的外部代码隔离开来。

    例如,如果您正在测试的代码调用 Web 服务,则很难全面测试 Web 服务调用可能失败的所有多种方式 - 超时、连接被拒绝、dns 解析失败等。在模拟 Web 服务接口中交换允许您的测试套件操作您的测试代码所依赖的数据提供者。您不是在测试数据提供者,而是想测试您的代码如何响应提供者返回的错误、不良数据和良好数据。所以模拟提供者并在测试套件中自己控制这些数据。

    【讨论】:

      【解决方案2】:

      您应该对添加和删除元素进行一些简单的测试。

      添加一组随机元素,然后验证 Count 属性作为您的测试的辅助函数没有任何问题。我不会费心从中返回任何其他值 - 如果 Add 因返回 false 而失败,那么辅助函数应该抛出异常以明确错误发生的位置。

      布尔返回值应单独测试。

      例如 1)创建一个集合并添加一个元素,然后断言该集合确实包含一个元素。 2)创建一个集合并添加一个元素,断言您放入的元素与您取出的元素相同 3) 如果 add 方法返回一个 bool,那么检查 Add 返回 true 和 Add 返回 false 的场景应该是另外两个测试用例。

      【讨论】:

      • 我永远不会为框架提供的功能编写测试。我有一个合理的期望,Add 方法可以正确添加元素。
      • 我同意没有必要测试框架功能——但是 OP 确实包含了“测试集合”的字样并通过了 ICollection,我的假设是他们正在测试他们自己的 ICollection 实现。
      • 对不起,如果我没有解释清楚,但我的目标是测试我的收藏。 :)
      【解决方案3】:

      我一开始误解了你的问题,我很抱歉。

      创建一个TestHelpers 类或项目来包含可重用代码以帮助您编写测试是完全合理的。例如,如果您使用测试数据集进行操作,您可以创建一些方法来获取 GetSampleTestDataForScenarioX,其中“ScenarioX”是您的场景的描述性名称。

      一般来说,单元测试应该包含很少的分支或循环方式。我遵循 Arrange/Act/Assert 测试模型,首先安排测试(实例化对象、设置测试条件等),然后对测试数据采取行动,然后断言我的条件得到满足。

      【讨论】:

        【解决方案4】:

        这是一个相当开放的问题,但这里有一些自以为是的建议,可以帮助您顺利上路。

        每个测试通常包含三个部分。

        a) 安排 - 设置要发送到您要测试的方法的值,创建可验证和可重现的环境。
        b) 行动 - 调用您的方法或任何您的测试
        c) 断言 - 确保您的方法完成了它应该做的事情,这意味着它返回了正确的值或产生了正确的副作用。

        出于个人喜好,我放了三个 cmets 来描绘这些部分 //Arrange //Act //Assert

        如果您的方法未设置为“可测试”,则安排有时会很棘手。开始研究有关依赖注入的大量信息。一旦你让你的东西可测试,就会有像MoqTypemockRhino Mocks这样的工具可以提供帮助。

        另一方面,我看到您有一种将随机数据加载到集合中的方法。测试应该很少有任何随机性。如果只是时间发生了变化,您不希望您的测试通过一天而在下一天失败。用您选择的常量填充数据并保持这种方式。要测试不同的数据,请加载不同的数据。不要让测试替你做。

        我刚刚谈到了几点。那里有大量关于如何测试代码的信息。最好的方法就是开始编写测试,边做边学。有测试总比没有测试好,好测试总比坏测试好。

        【讨论】:

          【解决方案5】:

          我在编写测试时看到的最大错误之一就是忘记将测试视为一流的代码。也就是说,将它们分解为易于维护和修改的方式,以便随着您的应用程序(以及扩展的测试)的发展。

          我见过的第二大错误是在每个测试中覆盖了太多代码。理想情况下,每个测试应该只测试几行代码。绝对不会比单个方法中包含的代码多。

          因此,如果您的示例中的 AddSomeElements 返回某些内容有意义,请让它返回一些内容。如果没有,不要。

          只要记住两件事:

          1. 您的测试是一流的代码
          2. 每个测试都应该只测试一小部分功能。

          【讨论】:

            猜你喜欢
            • 2012-11-17
            • 2011-02-10
            • 2013-10-10
            • 2016-12-12
            • 2010-11-14
            • 1970-01-01
            • 2015-11-04
            • 2020-10-14
            • 2018-05-17
            相关资源
            最近更新 更多