指南:使用测试资源管理器进行测试驱动开发

创建单元测试,以帮助通过增量代码更改使代码正常运行。 可以使用多个框架编写单元测试,包括第三方开发的一些框架。 某些测试框架专用于以不同语言或平台进行测试。 测试资源管理器为这些框架中的任何一个单元测试提供单个接口。 有关 测试资源管理器的详细信息,请参阅 使用测试资源管理器测试资源管理器运行单元测试常见问题解答

本演练演示如何使用 Microsoft Test Framework (MSTest) 在 C# 中开发测试方法。 可以轻松地针对其他语言或其他测试框架(如 NUnit)对其进行调整。 有关详细信息,请参阅 安装第三方单元测试框架

创建测试和生成代码

  1. 为 .NET 或 .NET Standard 创建 C# 类库 项目。 此项目将包含要测试的代码。 将项目命名 为 MyMath

  2. 在同一解决方案中,为 .NET 添加新 的 MSTest 测试项目。

    在 Visual Studio 2019 版本 16.9 中,MSTest 项目模板名称 单元测试项目

    将测试项目 命名为 MathTests

    新代码和测试项目

    新代码和测试项目

  3. 在测试项目中,编写一个简单的测试方法,用于验证为特定输入获取的结果。 将以下代码添加到 Test1UnitTest1 类:

    [TestMethod]
    public void BasicRooterTest()
    {
      // Create an instance to test:
      Rooter rooter = new Rooter();
      // Define a test input and output value:
      double expectedResult = 2.0;
      double input = expectedResult * expectedResult;
      // Run the method under test:
      double actualResult = rooter.SquareRoot(input);
      // Verify the result:
      Assert.AreEqual(expectedResult, actualResult, delta: expectedResult / 100);
    }
    
  4. 从测试代码生成类型。

    1. 将光标放在上 Rooter,然后打开灯泡菜单。

      选择“ 生成新类型”。

      生成新类型快速操作

      选择 生成“Rooter”类型>生成新类型

      生成新类型的快速操作

    2. 在“ 生成类型 ”对话框中,将 Project 设置为 MyMath,类库项目,然后选择 “确定”。

      Visual Studio 2019 中的“生成类型”对话框

      Visual Studio 2019 中的“生成类型”对话框

  5. 从测试代码生成方法。 将光标放在SquareRoot上,然后在灯泡菜单中,选择生成方法“SquareRoot”生成方法“Rooter.SquareRoot”

  6. 运行单元测试。

    1. 打开 测试资源管理器

      若要从 “测试 ”菜单中打开测试资源管理器,请选择 “测试资源管理器”。

      若要从 “测试 ”菜单中打开测试资源管理器,请选择 “Windows>测试资源管理器”。

    2. 测试资源管理器中,选择“ 全部运行 ”按钮以运行测试。

    解决方案生成,测试运行并失败。

  7. 选择测试的名称。

    测试的详细信息显示在 “测试详细信息摘要 ”窗格中。

    测试资源管理器中的测试详细信息摘要

    测试资源管理器中的测试详细信息摘要

  8. 选择 堆栈跟踪 下的顶部链接以跳转到测试失败的位置。

此时,你已创建了一个测试和一个存根,可以对其进行修改,以便测试通过。

验证代码更改

  1. Class1.cs 文件中,改进以下代码 SquareRoot

    public double SquareRoot(double input)
    {
        return input / 2;
    }
    
  2. 测试资源管理器中,选择“ 全部运行”。

    解决方案生成,测试运行并通过。

    测试浏览器显示通过测试

    测试浏览器显示通过的测试

扩展输入范围

为了增强我们对代码在所有情况下正常工作的信心,请添加测试以尝试更广泛的输入数据范围。

小窍门

避免更改通过的现有测试。 而是添加新的测试。 仅在用户要求发生更改时更改现有测试。 此策略有助于确保在扩展代码时不会丢失现有功能。

  1. 在测试类中,添加以下测试,该测试尝试输入值的范围:

    [TestMethod]
    public void RooterValueRange()
    {
        // Create an instance to test.
        Rooter rooter = new Rooter();
    
        // Try a range of values.
        for (double expected = 1e-8; expected < 1e+8; expected *= 3.2)
        {
            RooterOneValue(rooter, expected);
        }
    }
    
    private void RooterOneValue(Rooter rooter, double expectedResult)
    {
        double input = expectedResult * expectedResult;
        double actualResult = rooter.SquareRoot(input);
        Assert.AreEqual(expectedResult, actualResult, delta: expectedResult / 1000);
    }
    
  2. 测试资源管理器中,选择“ 全部运行”。

    新测试失败(尽管第一次测试仍然通过)。 若要查找失败点,请选择失败的测试,然后在 “测试详细信息摘要 ”窗格中查看详细信息。

  3. 检查测试中的方法,了解可能出了什么问题。 将 SquareRoot 代码更改为如下所示:

    public double SquareRoot(double input)
    {
      double result = input;
      double previousResult = -input;
      while (Math.Abs(previousResult - result) > result / 1000)
      {
        previousResult = result;
        result = result - (result * result - input) / (2 * result);
      }
      return result;
    }
    
  4. 测试资源管理器中,选择“ 全部运行”。

    这两个测试现在都通过。

添加针对异常案例的测试

  1. 为负输入添加一个新的测试。

    [TestMethod]
    public void RooterTestNegativeInput()
    {
        Rooter rooter = new Rooter();
        Assert.ThrowsException<ArgumentOutOfRangeException>(() => rooter.SquareRoot(-1));
    }
    
  2. 测试资源管理器中,选择“ 全部运行”。

    新测试失败。

    如果正在测试的方法出现循环,请选择取消选项位于测试资源管理器工具栏上。 测试停止执行并失败。

  3. SquareRoot代码修复方法是:在方法的开头添加以下if语句:

    public double SquareRoot(double input)
    {
        if (input <= 0.0)
        {
            throw new ArgumentOutOfRangeException();
        }
        ...
    
  4. 测试资源管理器中,选择“ 全部运行”。

    所有测试都通过。

重构受测试的代码

重构代码,但不更改测试。

小窍门

重构是一项更改,旨在使代码性能更好或更易于理解。 它不打算更改代码的行为,因此不会更改测试。

我们建议你独立于扩展功能的步骤执行重构步骤。 保持测试不变可让你确信在重构时没有意外引入 bug。

  1. SquareRoot方法中,修改用于计算result的那一行代码,如下所示:

    public double SquareRoot(double input)
    {
        if (input <= 0.0)
        {
            throw new ArgumentOutOfRangeException();
        }
    
        double result = input;
        double previousResult = -input;
        while (Math.Abs(previousResult - result) > result / 1000)
        {
            previousResult = result;
            result = (result + input / result) / 2;
            //was: result = result - (result * result - input) / (2*result);
        }
        return result;
    }
    
  2. 选择 “全部运行”,并验证所有测试是否仍通过。

    显示 3 个已通过的测试的测试资源管理器

    测试资源管理器显示 3 个已通过的测试