使用 MSTest 和 .NET 进行 C# 单元测试
本教程介绍分步构建示例解决方案的交互式体验,以了解单元测试概念。 如果希望使用预构建解决方案学习本教程,请在开始前查看或下载示例代码。 有关下载说明,请参阅示例和教程。
本文介绍如何测试 .NET Core 项目。 如果要测试 ASP.NET Core 项目,请参阅 ASP.NET Core 中的集成测试。
先决条件
创建源项目
打开 shell 窗口。 创建一个名为 unit-testing-using-mstest 的目录,用以保存解决方案。 在此新目录中,运行 dotnet new sln
为类库和测试项目创建新的解决方案文件。 创建 PrimeService 目录。 下图显示了当前的目录和文件结构:
/unit-testing-using-mstest
unit-testing-using-mstest.sln
/PrimeService
将 PrimeService 作为当前目录,然后运行 dotnet new classlib
以创建源项目。 将 Class1.cs 重命名为 PrimeService.cs。 将文件中的代码替换为以下代码,以创建 PrimeService
类的失败实现:
using System;
namespace Prime.Services
{
public class PrimeService
{
public bool IsPrime(int candidate)
{
throw new NotImplementedException("Please create a test first.");
}
}
}
将目录更改回 unit-testing-using-mstest 目录。 运行 dotnet sln add
以向解决方案添加类库项目:
dotnet sln add PrimeService/PrimeService.csproj
创建测试项目
创建 PrimeService.Tests 目录。 下图显示了它的目录结构:
/unit-testing-using-mstest
unit-testing-using-mstest.sln
/PrimeService
Source Files
PrimeService.csproj
/PrimeService.Tests
将 PrimeService.Tests 目录作为当前目录,并使用 dotnet new mstest
创建一个新项目。 dotnet 新命令会创建一个将 MSTest 用作测试库的测试项目。 模板在 PrimeServiceTests.csproj 文件中配置测试运行器:
<ItemGroup>
<PackageReference Include="MSTest" Version="3.2.0" />
<PackageReference Include="Microsoft.Testing.Extensions.CodeCoverage" Version="17.10.1" />
</ItemGroup>
测试项目需要其他包创建和运行单元测试。 上一步中的 dotnet new
添加了用于进行代码覆盖率报告的必要 MSTest 包和工具。
将 PrimeService
类库作为另一个依赖项添加到项目中。 使用 dotnet add reference
命令:
dotnet add reference ../PrimeService/PrimeService.csproj
可以在 GitHub 上的示例存储库中看到整个文件。
下图显示了最终的解决方案布局:
/unit-testing-using-mstest
unit-testing-using-mstest.sln
/PrimeService
Source Files
PrimeService.csproj
/PrimeService.Tests
Test Source Files
PrimeServiceTests.csproj
切换到 unit-testing-using-mstest 目录,然后运行 dotnet sln add
:
dotnet sln add ./PrimeService.Tests/PrimeService.Tests.csproj
创建第一个测试
编写失败测试,使其通过,然后重复此过程。 从 PrimeService.Tests 目录删除 UnitTest1.cs,并创建一个名为 PrimeService_IsPrimeShould.cs 且包含以下内容的新 C# 文件:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Prime.Services;
namespace Prime.UnitTests.Services
{
[TestClass]
public class PrimeService_IsPrimeShould
{
private readonly PrimeService _primeService;
public PrimeService_IsPrimeShould()
{
_primeService = new PrimeService();
}
[TestMethod]
public void IsPrime_InputIs1_ReturnFalse()
{
bool result = _primeService.IsPrime(1);
Assert.IsFalse(result, "1 should not be prime");
}
}
}
TestClass 属性表示包含单元测试的类。 TestMethod 属性指示方法是测试方法。
保存此文件并执行 dotnet test
以构建测试和类库,然后运行测试。 MSTest 测试运行程序包含要运行测试的程序入口点。 dotnet test
使用已创建的单元测试项目启动测试运行程序。
测试失败。 尚未创建实现。 在起作用的 PrimeService
类中编写最简单的代码,使此测试通过:
public bool IsPrime(int candidate)
{
if (candidate == 1)
{
return false;
}
throw new NotImplementedException("Please create a test first.");
}
在 unit-testing-using-mstest 目录中,再次运行 dotnet test
。 dotnet test
命令构建 PrimeService
项目,然后构建 PrimeService.Tests
项目。 构建这两个项目后,该命令将运行此单项测试。 测试通过。
添加更多功能
你已经通过了一个测试,现在可以编写更多测试。 质数有其他几种简单情况:0,-1。 可使用 TestMethod 属性添加新测试,但这很快就会变得枯燥乏味。 还有其他 MSTest 属性,使用这些属性可编写类似测试的套件。 测试方法可以执行相同的代码,但具有不同的输入参数。 可以使用 DataRow 属性来指定这些输入的值。
可以不使用这两个属性创建新测试,而用来创建单个数据驱动的测试。 数据驱动的测试方法用于测试多个小于 2(即最小质数)的值。 在 PrimeService_IsPrimeShould.cs 中添加新的测试方法:
[TestMethod]
[DataRow(-1)]
[DataRow(0)]
[DataRow(1)]
public void IsPrime_ValuesLessThan2_ReturnFalse(int value)
{
var result = _primeService.IsPrime(value);
Assert.IsFalse(result, $"{value} should not be prime");
}
运行 dotnet test
,两项测试均失败。 若要使所有测试通过,可以在 PrimeService.cs 文件中更改 IsPrime
方法开头的 if
子句:
if (candidate < 2)
通过在主库中添加更多测试、理论和代码继续循环访问。 你将拥有已完成的测试版本和库的完整实现。
你已生成一个小型库和该库的一组单元测试。 你已将解决方案结构化,使添加新包和新测试成为了正常工作流的一部分。 你已将多数的时间和精力集中在解决应用程序的目标上。