使用 MSTest 和 .NET Core 进行 C# 单元测试Unit testing C# with MSTest and .NET Core

本教程介绍分步构建示例解决方案的交互式体验,以了解单元测试概念。This tutorial takes you through an interactive experience building a sample solution step-by-step to learn unit testing concepts. 如果希望使用预构建解决方案学习本教程,请在开始前查看或下载示例代码If you prefer to follow the tutorial using a pre-built solution, view or download the sample code before you begin. 有关下载说明,请参阅示例和教程For download instructions, see Samples and Tutorials.

本文介绍如何测试 .NET Core 项目。This article is about testing a .NET Core project. 如果要测试 ASP.NET Core 项目,请参阅 ASP.NET Core 中的集成测试If you're testing an ASP.NET Core project, see Integration tests in ASP.NET Core.

创建源项目Create the source project

打开 shell 窗口。Open a shell window. 创建一个名为 unit-testing-using-mstest 的目录,用以保存解决方案 。Create a directory called unit-testing-using-mstest to hold the solution. 在此新目录中,运行 dotnet new sln 为类库和测试项目创建新的解决方案文件。Inside this new directory, run dotnet new sln to create a new solution file for the class library and the test project. 接下来,创建 PrimeService 目录。Next, create a PrimeService directory. 下图显示了当前的目录和文件结构:The following outline shows the directory and file structure thus far:

/unit-testing-using-mstest
    unit-testing-using-mstest.sln
    /PrimeService

PrimeService 作为当前目录,然后运行 dotnet new classlib 以创建源项目。Make PrimeService the current directory and run dotnet new classlib to create the source project. Class1.cs 重命名为 PrimeService.csRename Class1.cs to PrimeService.cs. 创建 PrimeService 类的失败实现:You create a failing implementation of the PrimeService class:

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 目录。Change the directory back to the unit-testing-using-mstest directory. 运行 dotnet sln add PrimeService/PrimeService.csproj 向解决方案添加类库项目。Run dotnet sln add PrimeService/PrimeService.csproj to add the class library project to the solution.

创建测试项目Create the test project

接下来,创建 PrimeService.Tests 目录。Next, create the PrimeService.Tests directory. 下图显示了它的目录结构:The following outline shows the directory structure:

/unit-testing-using-mstest
    unit-testing-using-mstest.sln
    /PrimeService
        Source Files
        PrimeService.csproj
    /PrimeService.Tests

PrimeService.Tests 目录作为当前目录,并使用 dotnet new mstest 创建一个新项目。Make the PrimeService.Tests directory the current directory and create a new project using dotnet new mstest. dotnet 新命令会创建一个将 MSTest 用作测试库的测试项目。The dotnet new command creates a test project that uses MSTest as the test library. 生成的模板在 PrimeServiceTests.csproj 文件中配置测试运行程序:The generated template configures the test runner in the PrimeServiceTests.csproj file:

<ItemGroup>
  <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
  <PackageReference Include="MSTest.TestAdapter" Version="1.1.18" />
  <PackageReference Include="MSTest.TestFramework" Version="1.1.18" />
</ItemGroup>

测试项目需要其他包创建和运行单元测试。The test project requires other packages to create and run unit tests. 上一步中的 dotnet new 添加了 MSTest SDK、MSTest 测试框架和 MSTest 运行程序。dotnet new in the previous step added the MSTest SDK, the MSTest test framework, and the MSTest runner. 现在,将 PrimeService 类库作为另一个依赖项添加到项目中。Now, add the PrimeService class library as another dependency to the project. 使用 dotnet add reference 命令:Use the dotnet add reference command:

dotnet add reference ../PrimeService/PrimeService.csproj

可以在 GitHub 上的示例存储库中看到整个文件。You can see the entire file in the samples repository on GitHub.

下图显示了最终的解决方案布局:The following outline shows the final solution layout:

/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 .\PrimeService.Tests\PrimeService.Tests.csprojExecute dotnet sln add .\PrimeService.Tests\PrimeService.Tests.csproj in the unit-testing-using-mstest directory.

创建第一个测试Create the first test

编写一个失败测试,使其通过,然后重复此过程。You write one failing test, make it pass, then repeat the process. PrimeService.Tests 目录删除 UnitTest1.cs,并创建一个名为 PrimeService_IsPrimeShould.cs 且包含以下内容的新 C# 文件:Remove UnitTest1.cs from the PrimeService.Tests directory and create a new C# file named PrimeService_IsPrimeShould.cs with the following content:

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()
        {
            var result = _primeService.IsPrime(1);

            Assert.IsFalse(result, "1 should not be prime");
        }
    }
}

TestClass 属性表示包含单元测试的类。The TestClass attribute denotes a class that contains unit tests. TestMethod 属性指示方法是测试方法。The TestMethod attribute indicates a method is a test method.

保存此文件并执行 dotnet test 以构建测试和类库,然后运行测试。Save this file and execute dotnet test to build the tests and the class library and then run the tests. MSTest 测试运行程序包含要运行测试的程序入口点。The MSTest test runner contains the program entry point to run your tests. dotnet test 使用已创建的单元测试项目启动测试运行程序。dotnet test starts the test runner using the unit test project you've created.

测试失败。Your test fails. 尚未创建实现。You haven't created the implementation yet. 在起作用的 PrimeService 类中编写最简单的代码,使此测试通过:Make this test pass by writing the simplest code in the PrimeService class that works:

public bool IsPrime(int candidate)
{
    if (candidate == 1)
    {
        return false;
    }
    throw new NotImplementedException("Please create a test first.");
}

在 unit-testing-using-mstest 目录中,再次运行 dotnet testIn the unit-testing-using-mstest directory, run dotnet test again. dotnet test 命令构建 PrimeService 项目,然后构建 PrimeService.Tests 项目。The dotnet test command runs a build for the PrimeService project and then for the PrimeService.Tests project. 构建这两个项目后,该命令将运行此单项测试。After building both projects, it runs this single test. 测试通过。It passes.

添加更多功能Add more features

你已经通过了一个测试,现在可以编写更多测试。Now that you've made one test pass, it's time to write more. 质数有其他几种简单情况:0,-1。There are a few other simple cases for prime numbers: 0, -1. 可使用 TestMethod 属性添加新测试,但这很快就会变得枯燥乏味。You could add new tests with the TestMethod attribute, but that quickly becomes tedious. 还有其他 MSTest 属性,使用这些属性可编写类似测试的套件。There are other MSTest attributes that enable you to write a suite of similar tests. DataTestMethod 属性表示执行相同代码但具有不同输入参数的测试套件。A DataTestMethod attribute represents a suite of tests that execute the same code but have different input arguments. 可以使用 DataRow 属性来指定这些输入的值。You can use the DataRow attribute to specify values for those inputs.

可以不使用这两个属性创建新测试,而用来创建单个数据驱动的测试。Instead of creating new tests, apply these two attributes to create a single data driven test. 数据驱动的测试方法用于测试多个小于 2(即最小质数)的值:The data driven test is a method that tests several values less than two, which is the lowest prime number:

[DataTestMethod]
[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,两项测试均失败。Run dotnet test, and two of these tests fail. 若要使所有测试通过,可以更改方法开头的 if 子句:To make all of the tests pass, change the if clause at the beginning of the method:

if (candidate < 2)

通过在主库中添加更多测试、理论和代码继续循环访问。Continue to iterate by adding more tests, more theories, and more code in the main library. 你将拥有已完成的测试版本库的完整实现You have the finished version of the tests and the complete implementation of the library.

你已生成一个小型库和该库的一组单元测试。You've built a small library and a set of unit tests for that library. 你已将解决方案结构化,使添加新包和新测试成为了正常工作流的一部分。You've structured the solution so that adding new packages and tests is part of the normal workflow. 你已将多数的时间和精力集中在解决应用程序的目标上。You've concentrated most of your time and effort on solving the goals of the application.

另请参阅See also