使用 NUnit 和 .NET Core 进行 C# 单元测试Unit testing C# with NUnit 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.

系统必备Prerequisites

  • .NET Core 2.1 SDK 或更高版本。.NET Core 2.1 SDK or later versions.
  • 按需选择的文本编辑器或代码编辑器。A text editor or code editor of your choice.

创建源项目Creating the source project

打开 shell 窗口。Open a shell window. 创建一个名为 unit-testing-using-nunit 的目录,以保留该解决方案。Create a directory called unit-testing-using-nunit to hold the solution. 在此新目录中,运行以下命令,为类库和测试项目创建新的解决方案文件:Inside this new directory, run the following command to create a new solution file for the class library and the test project:

dotnet new sln

接下来,创建 PrimeService 目录。Next, create a PrimeService directory. 下图显示了当前的目录和文件结构:The following outline shows the directory and file structure so far:

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

将 PrimeService 作为当前目录,并运行以下命令以创建源项目:Make PrimeService the current directory and run the following command to create the source project:

dotnet new classlib

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-nunit 目录。Change the directory back to the unit-testing-using-nunit directory. 运行以下命令,向解决方案添加类库项目:Run the following command to add the class library project to the solution:

dotnet sln add PrimeService/PrimeService.csproj

创建测试项目Creating the test project

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

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

将 PrimeService.Tests 目录作为当前目录,并使用以下命令创建一个新项目:Make the PrimeService.Tests directory the current directory and create a new project using the following command:

dotnet new nunit

dotnet new 命令可创建一个将 NUnit 用作测试库的测试项目。The dotnet new command creates a test project that uses NUnit as the test library. 生成的模板在 PrimeService.Tests.csproj 文件中配置测试运行程序:The generated template configures the test runner in the PrimeService.Tests.csproj file:

<ItemGroup>
  <PackageReference Include="nunit" Version="3.12.0" />
  <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
  <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0" />
</ItemGroup>

测试项目需要其他包创建和运行单元测试。The test project requires other packages to create and run unit tests. 在上一步中,dotnet new 已添加 Microsoft 测试 SDK、NUnit 测试框架和 NUnit 测试适配器。dotnet new in the previous step added the Microsoft test SDK, the NUnit test framework, and the NUnit test adapter. 现在,将 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-nunit
    unit-testing-using-nunit.sln
    /PrimeService
        Source Files
        PrimeService.csproj
    /PrimeService.Tests
        Test Source Files
        PrimeService.Tests.csproj

在 unit-testing-using-nunit 目录中执行以下命令:Execute the following command in the unit-testing-using-nunit directory:

dotnet sln add ./PrimeService.Tests/PrimeService.Tests.csproj

创建第一个测试Creating the first test

编写一个失败测试,使其通过,然后重复此过程。You write one failing test, make it pass, then repeat the process. 在 PrimeService.Tests 目录中,将 UnitTest1.cs 文件重命名为 PrimeService_IsPrimeShould.cs ,并将其整个内容替换为以下代码:In the PrimeService.Tests directory, rename the UnitTest1.cs file to PrimeService_IsPrimeShould.cs and replace its entire contents with the following code:

using NUnit.Framework;
using Prime.Services;

namespace Prime.UnitTests.Services
{
    [TestFixture]
    public class PrimeService_IsPrimeShould
    {
        private PrimeService _primeService;

        [SetUp]
        public void SetUp()
        {
            _primeService = new PrimeService();
        }

        [Test]
        public void IsPrime_InputIs1_ReturnFalse()
        {
            var result = _primeService.IsPrime(1);

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

[TestFixture] 属性表示包含单元测试的类。The [TestFixture] attribute denotes a class that contains unit tests. [Test] 属性指示方法是测试方法。The [Test] 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. NUnit 测试运行程序包含要运行测试的程序入口点。The NUnit 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-nunit 目录中再次运行 dotnet testIn the unit-testing-using-nunit 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.

添加更多功能Adding 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. 可以添加具有 [Test] 属性的新测试,但这很快就会变得枯燥乏味。You could add new tests with the [Test] attribute, but that quickly becomes tedious. 还有其他 NUnit 属性可用于编写一套类似的测试。There are other NUnit attributes that enable you to write a suite of similar tests. [TestCase] 属性用于创建一套可执行相同代码但具有不同输入参数的测试。A [TestCase] attribute is used to create a suite of tests that execute the same code but have different input arguments. 可以使用 [TestCase] 属性来指定这些输入的值。You can use the [TestCase] attribute to specify values for those inputs.

无需创建新的测试,而是应用此属性来创建数据驱动的单个测试。Instead of creating new tests, apply this attribute 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:

[TestCase(-1)]
[TestCase(0)]
[TestCase(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. 若要使所有测试通过,可以在 PrimeService.cs 文件中更改 Main 方法开头的 if 子句:To make all of the tests pass, change the if clause at the beginning of the Main method in the PrimeService.cs file:

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.