使用 dotnet test 和 xUnit 在 .NET Core 中进行 C# 单元测试Unit testing C# in .NET Core using dotnet test and xUnit

本教程演示如何生成包含单元测试项目和源代码项目的解决方案。This tutorial shows how to build a solution containing a unit test project and source code project. 若要使用预构建解决方案学习本教程,请查看或下载示例代码To follow the tutorial using a pre-built solution, view or download the sample code. 有关下载说明,请参阅示例和教程For download instructions, see Samples and Tutorials.

创建解决方案Create the solution

在本部分中,将创建包含源和测试项目的解决方案。In this section, a solution is created that contains the source and test projects. 已完成的解决方案具有以下目录结构:The completed solution has the following directory structure:

/unit-testing-using-dotnet-test
    unit-testing-using-dotnet-test.sln
    /PrimeService
        PrimeService.cs
        PrimeService.csproj
    /PrimeService.Tests
        PrimeService_IsPrimeShould.cs
        PrimeServiceTests.csproj

以下说明提供了创建测试解决方案的步骤。The following instructions provide the steps to create the test solution. 有关通过一个步骤创建测试解决方案的说明,请参阅用于创建测试解决方案的命令See Commands to create test solution for instructions to create the test solution in one step.

  • 打开 shell 窗口。Open a shell window.

  • 运行下面的命令:Run the following command:

    dotnet new sln -o unit-testing-using-dotnet-test
    

    dotnet new sln 命令用于在 unit-testing-using-dotnet-test 目录中创建新的解决方案。The dotnet new sln command creates a new solution in the unit-testing-using-dotnet-test directory.

  • 将目录更改为 unit-testing-using-dotnet-test 文件夹。Change directory to the unit-testing-using-dotnet-test folder.

  • 运行下面的命令:Run the following command:

    dotnet new classlib -o PrimeService
    

    dotnet new classlib 命令用于在 PrimeService 文件夹中创建新的类库项目。The dotnet new classlib command creates a new class library project in the PrimeService folder. 新的类库将包含要测试的代码。The new class library will contain the code to be tested.

  • Class1.cs 重命名为 PrimeService.csRename Class1.cs to PrimeService.cs.

  • 将 PrimeService.cs 中的代码替换为以下代码:Replace the code in PrimeService.cs with the following code:

    using System;
    
    namespace Prime.Services
    {
        public class PrimeService
        {
            public bool IsPrime(int candidate)
            {
                throw new NotImplementedException("Not implemented.");
            }
        }
    }
    
  • 前面的代码:The preceding code:

  • 在 unit-testing-using-dotnet-test 目录下运行以下命令,向解决方案添加类库项目:In the unit-testing-using-dotnet-test directory, run the following command to add the class library project to the solution:

    dotnet sln add ./PrimeService/PrimeService.csproj
    
  • 运行以下命令创建 PrimeService.Tests 项目:Create the PrimeService.Tests project by running the following command:

    dotnet new xunit -o PrimeService.Tests
    
  • 上面的命令:The preceding command:

    • 在 PrimeService.Tests 目录中创建 PrimeService.Tests 项目。Creates the PrimeService.Tests project in the PrimeService.Tests directory. 测试项目将 xUnit 用作测试库。The test project uses xUnit as the test library.
    • 通过将以下 <PackageReference /> 元素添加到项目文件来配置测试运行程序:Configures the test runner by adding the following <PackageReference />elements to the project file:
      • “Microsoft.NET.Test.Sdk”"Microsoft.NET.Test.Sdk"
      • “xunit”"xunit"
      • “xunit.runner.visualstudio”"xunit.runner.visualstudio"
  • 运行以下命令将测试项目添加到解决方案文件:Add the test project to the solution file by running the following command:

    dotnet sln add ./PrimeService.Tests/PrimeService.Tests.csproj
    
  • PrimeService 类库作为一个依赖项添加到 PrimeService.Tests 项目中:Add the PrimeService class library as a dependency to the PrimeService.Tests project:

    dotnet add ./PrimeService.Tests/PrimeService.Tests.csproj reference ./PrimeService/PrimeService.csproj  
    

用于创建解决方案的命令Commands to create the solution

本部分汇总了上一部分中的所有命令。This section summarizes all the commands in the previous section. 如果已完成上一部分中的步骤,请跳过本部分。Skip this section if you've completed the steps in the previous section.

以下命令用于在 Windows 计算机上创建测试解决方案。The following commands create the test solution on a windows machine. 对于 macOS 和 Unix,请将 ren 命令更新为 OS 版本的 ren 以重命名文件:For macOS and Unix, update the ren command to the OS version of ren to rename a file:

dotnet new sln -o unit-testing-using-dotnet-test
cd unit-testing-using-dotnet-test
dotnet new classlib -o PrimeService
ren .\PrimeService\Class1.cs PrimeService.cs
dotnet sln add ./PrimeService/PrimeService.csproj
dotnet new xunit -o PrimeService.Tests
dotnet add ./PrimeService.Tests/PrimeService.Tests.csproj reference ./PrimeService/PrimeService.csproj
dotnet sln add ./PrimeService.Tests/PrimeService.Tests.csproj

请按照上一部分中的“将 PrimeService.cs 中的代码替换为以下代码”的说明进行操作。Follow the instructions for "Replace the code in PrimeService.cs with the following code" in the previous section.

创建测试Create a test

测试驱动开发 (TDD) 中的一种常用方法是在实现目标代码之前编写测试。A popular approach in test driven development (TDD) is to write a test before implementing the target code. 本教程使用 TDD 方法。This tutorial uses the TDD approach. IsPrime 方法可调用,但未实现。The IsPrime method is callable, but not implemented. IsPrime 的测试调用失败。A test call to IsPrime fails. 对于 TDD,会编写已知失败的测试。With TDD, a test is written that is known to fail. 更新目标代码使测试通过。The target code is updated to make the test pass. 你可以重复使用此方法,编写失败的测试,然后更新目标代码使测试通过。You keep repeating this approach, writing a failing test and then updating the target code to pass.

更新 PrimeService.Tests 项目:Update the PrimeService.Tests project:

  • 删除 PrimeService.Tests/UnitTest1.cs。Delete PrimeService.Tests/UnitTest1.cs.
  • 创建 PrimeService.Tests/PrimeService_IsPrimeShould.cs 文件。Create a PrimeService.Tests/PrimeService_IsPrimeShould.cs file.
  • 将 PrimeService_IsPrimeShould.cs 中的代码替换为以下代码:Replace the code in PrimeService_IsPrimeShould.cs with the following code:
using Xunit;
using Prime.Services;

namespace Prime.UnitTests.Services
{
    public class PrimeService_IsPrimeShould
    {
        private readonly PrimeService _primeService;

        public PrimeService_IsPrimeShould()
        {
            _primeService = new PrimeService();
        }

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

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

[Fact] 属性声明由测试运行程序运行的测试方法。The [Fact] attribute declares a test method that's run by the test runner. 从 PrimeService.Tests 文件夹运行 dotnet testFrom the PrimeService.Tests folder, run dotnet test. dotnet test 命令生成两个项目并运行测试。The dotnet test command builds both projects and runs the tests. xUnit 测试运行程序包含要运行测试的程序入口点。The xUnit test runner contains the program entry point to run the tests. dotnet test 使用单元测试项目启动测试运行程序。dotnet test starts the test runner using the unit test project.

测试失败,因为尚未实现 IsPrimeThe test fails because IsPrime hasn't been implemented. 使用 TDD 方法,只需编写足够的代码即可使此测试通过。Using the TDD approach, write only enough code so this test passes. 使用以下代码更新 IsPrimeUpdate IsPrime with the following code:

public bool IsPrime(int candidate)
{
    if (candidate == 1)
    {
        return false;
    }
    throw new NotImplementedException("Not fully implemented.");
}

运行 dotnet testRun dotnet test. 测试通过。The test passes.

添加更多测试Add more tests

为 0 和 -1 添加素数测试。Add prime number tests for 0 and -1. 你可以复制上述测试并将以下代码更改为使用 0 和 -1:You could copy the preceding test and change the following code to use 0 and -1:

var result = _primeService.IsPrime(1);

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

仅当参数更改代码重复和测试膨胀中的结果时复制测试代码。Copying test code when only a parameter changes results in code duplication and test bloat. 以下 xUnit 属性允许编写类似测试套件:The following xUnit attributes enable writing a suite of similar tests:

  • [Theory] 表示执行相同代码,但具有不同输入参数的测试套件。[Theory] represents a suite of tests that execute the same code but have different input arguments.
  • [InlineData] 属性指定这些输入的值。[InlineData] attribute specifies values for those inputs.

可以不使用上述 xUnit 属性创建新测试,而是用来创建单个索引。Rather than creating new tests, apply the preceding xUnit attributes to create a single theory. 替换以下代码:Replace the following code:

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

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

替换为以下代码:with the following code:

[Theory]
[InlineData(-1)]
[InlineData(0)]
[InlineData(1)]
public void IsPrime_ValuesLessThan2_ReturnFalse(int value)
{
    var result = _primeService.IsPrime(value);

    Assert.False(result, $"{value} should not be prime");
}

在前面的代码中,[Theory][InlineData] 允许测试多个小于 2 的值。In the preceding code, [Theory] and [InlineData] enable testing several values less than two. 2 是最小的素数。Two is the smallest prime number.

运行 dotnet test,其中两个测试失败。Run dotnet test, two of the tests fail. 若要使所有测试通过,请使用以下代码更新 IsPrime 方法:To make all of the tests pass, update the IsPrime method with the following code:

public bool IsPrime(int candidate)
{
    if (candidate < 2)
    {
        return false;
    }
    throw new NotImplementedException("Not fully implemented.");
}

遵循 TDD 方法,添加更多失败的测试,然后更新目标代码。Following the TDD approach, add more failing tests, then update the target code. 请参阅已完成的测试版本库的完整实现See the finished version of the tests and the complete implementation of the library.

已完成的 IsPrime 方法不是用于测试素性的有效算法。The completed IsPrime method is not an efficient algorithm for testing primality.

其他资源Additional resources