创建单元测试,以帮助通过增量代码更改使代码正常运行。 可以使用多个框架编写单元测试,包括第三方开发的一些框架。 某些测试框架专用于以不同语言或平台进行测试。 测试资源管理器为这些框架中的任何一个单元测试提供单个接口。 有关 测试资源管理器的详细信息,请参阅 使用测试资源管理器 和 测试资源管理器运行单元测试常见问题解答。
本演练演示如何使用 Microsoft Test Framework (MSTest) 在 C# 中开发测试方法。 可以轻松地针对其他语言或其他测试框架(如 NUnit)对其进行调整。 有关详细信息,请参阅 安装第三方单元测试框架。
创建测试和生成代码
为 .NET 或 .NET Standard 创建 C# 类库 项目。 此项目将包含要测试的代码。 将项目命名 为 MyMath。
在同一解决方案中,为 .NET 添加新 的 MSTest 测试项目。
在 Visual Studio 2019 版本 16.9 中,MSTest 项目模板名称 单元测试项目。
将测试项目 命名为 MathTests。
在测试项目中,编写一个简单的测试方法,用于验证为特定输入获取的结果。 将以下代码添加到
Test1
或UnitTest1
类:[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); }
从测试代码生成类型。
将光标放在上
Rooter
,然后打开灯泡菜单。选择“ 生成新类型”。
选择 生成“Rooter”类型>生成新类型。
在“ 生成类型 ”对话框中,将 Project 设置为 MyMath,类库项目,然后选择 “确定”。
从测试代码生成方法。 将光标放在
SquareRoot
上,然后在灯泡菜单中,选择生成方法“SquareRoot”或生成方法“Rooter.SquareRoot”。运行单元测试。
打开 测试资源管理器。
若要从 “测试 ”菜单中打开测试资源管理器,请选择 “测试资源管理器”。
若要从 “测试 ”菜单中打开测试资源管理器,请选择 “Windows>测试资源管理器”。
在 测试资源管理器中,选择“ 全部运行 ”按钮以运行测试。
解决方案生成,测试运行并失败。
选择测试的名称。
测试的详细信息显示在 “测试详细信息摘要 ”窗格中。
选择 堆栈跟踪 下的顶部链接以跳转到测试失败的位置。
此时,你已创建了一个测试和一个存根,可以对其进行修改,以便测试通过。
验证代码更改
在 Class1.cs 文件中,改进以下代码
SquareRoot
:public double SquareRoot(double input) { return input / 2; }
在 测试资源管理器中,选择“ 全部运行”。
解决方案生成,测试运行并通过。
扩展输入范围
为了增强我们对代码在所有情况下正常工作的信心,请添加测试以尝试更广泛的输入数据范围。
小窍门
避免更改通过的现有测试。 而是添加新的测试。 仅在用户要求发生更改时更改现有测试。 此策略有助于确保在扩展代码时不会丢失现有功能。
在测试类中,添加以下测试,该测试尝试输入值的范围:
[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); }
在 测试资源管理器中,选择“ 全部运行”。
新测试失败(尽管第一次测试仍然通过)。 若要查找失败点,请选择失败的测试,然后在 “测试详细信息摘要 ”窗格中查看详细信息。
检查测试中的方法,了解可能出了什么问题。 将
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; }
在 测试资源管理器中,选择“ 全部运行”。
这两个测试现在都通过。
添加针对异常案例的测试
为负输入添加一个新的测试。
[TestMethod] public void RooterTestNegativeInput() { Rooter rooter = new Rooter(); Assert.ThrowsException<ArgumentOutOfRangeException>(() => rooter.SquareRoot(-1)); }
在 测试资源管理器中,选择“ 全部运行”。
新测试失败。
如果正在测试的方法出现循环,请选择取消选项位于测试资源管理器工具栏上。 测试停止执行并失败。
SquareRoot
代码修复方法是:在方法的开头添加以下if
语句:public double SquareRoot(double input) { if (input <= 0.0) { throw new ArgumentOutOfRangeException(); } ...
在 测试资源管理器中,选择“ 全部运行”。
所有测试都通过。
重构受测试的代码
重构代码,但不更改测试。
小窍门
重构是一项更改,旨在使代码性能更好或更易于理解。 它不打算更改代码的行为,因此不会更改测试。
我们建议你独立于扩展功能的步骤执行重构步骤。 保持测试不变可让你确信在重构时没有意外引入 bug。
在
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; }
选择 “全部运行”,并验证所有测试是否仍通过。