使用 .NET Core 命令行组织和测试项目Organizing and testing projects with the .NET Core command line

本文内容

本教程遵循通过命令行开始在 Windows/Linux/macOS 上使用 .NET Core,不只是介绍了如何创建简单的控制台应用程序,还介绍了如何开发结构清晰的高级应用程序。在演示如何使用文件夹来组织代码后,本教程还将说明如何使用 xUnit 测试框架扩展控制台应用程序。

使用文件夹组织代码Using folders to organize code

如要要在控制台应用中引入新类型,可向该应用添加包含该类型的文件。例如,如果向项目添加包含 AccountInformationMonthlyReportRecords 类型的文件,则项目文件结构是平面的,且易于导航:

  1. /MyProject
  2. |__AccountInformation.cs
  3. |__MonthlyReportRecords.cs
  4. |__MyProject.csproj
  5. |__Program.cs

但是,仅在项目规模相对较小时,此方法才适用。你能否想象在项目中添加 20 个类型时会发生什么?项目的根目录中会散落许多文件,这样的项目必然会难以导航和维护。

要组织项目,请创建一个名为 Models 新文件夹,将其用于保存类型文件。将类型文件放入 Models 文件夹中:

  1. /MyProject
  2. |__/Models
  3. |__AccountInformation.cs
  4. |__MonthlyReportRecords.cs
  5. |__MyProject.csproj
  6. |__Program.cs

按逻辑将文件分组到文件夹的项目易于导航和维护。在下一节中,将创建一个更复杂的示例,它包含文件夹和单元测试。

使用 NewTypes Pets 示例进行组织和测试Organizing and testing using the NewTypes Pets Sample

生成示例Building the sample

对于下列步骤,可使用 NewTypes Pets 示例进行相关操作,也可以创建自己的文件与文件夹进行操作。各类型按逻辑组织为文件夹结构,允许日后加入更多类型,测试也按逻辑放置在文件夹中,允许日后加入更多测试。

此示例包含两种类型 DogCat,并使它们实现一个公共接口 IPet对于 NewTypes 项目,目标是将与宠物相关的类型组织到 Pets 文件夹中。如果之后添加了另一组类型(例如 WildAnimals ),则将其与 Pets 文件夹一同放在 NewTypes 文件夹中。WildAnimals 文件夹可包含不属于宠物的动物类型,如 SquirrelRabbit 类型。按照这种方式添加类型,不会破坏项目的良好组织。

创建以下文件夹结构,并指明文件内容:

  1. /NewTypes
  2. |__/src
  3. |__/NewTypes
  4. |__/Pets
  5. |__Dog.cs
  6. |__Cat.cs
  7. |__IPet.cs
  8. |__Program.cs
  9. |__NewTypes.csproj

IPet.cs :

  1. using System;
  2. namespace Pets
  3. {
  4. public interface IPet
  5. {
  6. string TalkToOwner();
  7. }
  8. }

Dog.cs :

  1. using System;
  2. namespace Pets
  3. {
  4. public class Dog : IPet
  5. {
  6. public string TalkToOwner() => "Woof!";
  7. }
  8. }

Cat.cs :

  1. using System;
  2. namespace Pets
  3. {
  4. public class Cat : IPet
  5. {
  6. public string TalkToOwner() => "Meow!";
  7. }
  8. }

Program.cs :

  1. using System;
  2. using Pets;
  3. using System.Collections.Generic;
  4. namespace ConsoleApplication
  5. {
  6. public class Program
  7. {
  8. public static void Main(string[] args)
  9. {
  10. List<IPet> pets = new List<IPet>
  11. {
  12. new Dog(),
  13. new Cat()
  14. };
  15. foreach (var pet in pets)
  16. {
  17. Console.WriteLine(pet.TalkToOwner());
  18. }
  19. }
  20. }
  21. }

NewTypes.csproj :

  1. <Project Sdk="Microsoft.NET.Sdk">
  2. <PropertyGroup>
  3. <OutputType>Exe</OutputType>
  4. <TargetFramework>netcoreapp2.2</TargetFramework>
  5. </PropertyGroup>
  6. </Project>

请执行以下命令:

dotnet run

获得以下输出:

Woof!
Meow!

可选练习:可通过扩展此项目来添加新的宠物类型,例如 Bird使鸟的 TalkToOwner 方法向所有者发出 Tweet!再次运行应用。输出将包含 Tweet!

测试示例Testing the sample

NewTypes 项目已准备就绪,与宠物相关的类型均置于一个文件夹中,因此具有良好的组织。接下来,创建测试项目,并使用 xUnit 测试框架开始编写测试。使用单元测试,可自动检查宠物类型的行为,确认其正常运行。

导航回 src 文件夹并创建“test”文件夹,后者包含 NewTypesTests 文件夹 。在 NewTypesTests 文件夹的命令提示符中,执行 dotnet new xunit这将生成两个文件:NewTypesTests.csproj 和 UnitTest1.cs 。

测试项目当前无法测试 NewTypes 中的类型,并且需要对 NewTypes 项目的项目引用。要添加项目引用,请使用 dotnet add reference 命令:

dotnet add reference ../../src/NewTypes/NewTypes.csproj

或者,可以选择向 NewTypesTests.csproj 文件添加 <ItemGroup> 节点,手动添加项目引用 :

<ItemGroup>
  <ProjectReference Include="../../src/NewTypes/NewTypes.csproj" />
</ItemGroup>

NewTypesTests.csproj :

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp2.2</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
    <PackageReference Include="xunit" Version="2.4.1" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="../../src/NewTypes/NewTypes.csproj"/>
  </ItemGroup>

</Project>

NewTypesTests.csproj 文件包含下列内容:

  • 对 .NET 测试基础结构 Microsoft.NET.Test.Sdk 的包引用
  • 对 xUnit 测试框架 xunit 的包引用
  • 对测试运行程序 xunit.runner.visualstudio 的包引用
  • 对要测试的代码 NewTypes 的项目引用将 UnitTest1.cs 的名称更改为 PetTests.cs ,并将文件中的代码替换为下列内容:
using System;
using Xunit;
using Pets;

public class PetTests
{
    [Fact]
    public void DogTalkToOwnerReturnsWoof()
    {
        string expected = "Woof!";
        string actual = new Dog().TalkToOwner();

        Assert.NotEqual(expected, actual);
    }

    [Fact]
    public void CatTalkToOwnerReturnsMeow()
    {
        string expected = "Meow!";
        string actual = new Cat().TalkToOwner();

        Assert.NotEqual(expected, actual);
    }
}

可选练习:如果先前向所有者添加了生成 Tweet!Bird 类型,请向 PetTests.cs 文件 BirdTalkToOwnerReturnsTweet 添加测试方法,以检查对于 Bird 类型,TalkToOwner 方法是否正常工作 。

备注

尽管期望 expectedactual 值相等,但使用 Assert.NotEqual 检查的初始断言表明这些值并不相等 。务必最初创建一个失败的测试,以检查测试的逻辑是否正确。确认测试失败后,调整断言,使测试通过。

下面演示了完整的项目结构:

/NewTypes
|__/src
   |__/NewTypes
      |__/Pets
         |__Dog.cs
         |__Cat.cs
         |__IPet.cs
      |__Program.cs
      |__NewTypes.csproj
|__/test
   |__NewTypesTests
      |__PetTests.cs
      |__NewTypesTests.csproj

在 test/NewTypesTests 目录中开始。使用 dotnet restore 命令还原测试项目。使用 dotnet test 命令运行测试。此命令启动项目文件中指定的测试运行程序。

备注

从 .NET Core 2.0 SDK 开始,无需运行 dotnet restore,因为它由所有需要还原的命令隐式运行,如 dotnet newdotnet builddotnet run在执行显式还原有意义的某些情况下,例如 Azure DevOps Services 中的持续集成生成中,或在需要显式控制还原发生时间的生成系统中,它仍然是有效的命令。

测试按预期失败,控制台显示以下输出:

Test run for c:\Users\ronpet\repos\samples\core\console-apps\NewTypesMsBuild\test\NewTypesTests\bin\Debug\netcoreapp2.1\NewTypesTests.dll(.NETCoreApp,Version=v2.1)
Microsoft (R) Test Execution Command Line Tool Version 15.8.0
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...
[xUnit.net 00:00:00.77]     PetTests.DogTalkToOwnerReturnsWoof [FAIL]
[xUnit.net 00:00:00.78]     PetTests.CatTalkToOwnerReturnsMeow [FAIL]
Failed   PetTests.DogTalkToOwnerReturnsWoof
Error Message:
 Assert.NotEqual() Failure
Expected: Not "Woof!"
Actual:   "Woof!"
Stack Trace:
   at PetTests.DogTalkToOwnerReturnsWoof() in c:\Users\ronpet\repos\samples\core\console-apps\NewTypesMsBuild\test\NewTypesTests\PetTests.cs:line 13
Failed   PetTests.CatTalkToOwnerReturnsMeow
Error Message:
 Assert.NotEqual() Failure
Expected: Not "Meow!"
Actual:   "Meow!"
Stack Trace:
   at PetTests.CatTalkToOwnerReturnsMeow() in c:\Users\ronpet\repos\samples\core\console-apps\NewTypesMsBuild\test\NewTypesTests\PetTests.cs:line 22

Total tests: 2. Passed: 0. Failed: 2. Skipped: 0.
Test Run Failed.
Test execution time: 1.7000 Seconds

将测试的断言从 Assert.NotEqual 更改为 Assert.Equal

using System;
using Xunit;
using Pets;

public class PetTests
{
    [Fact]
    public void DogTalkToOwnerReturnsWoof()
    {
        string expected = "Woof!";
        string actual = new Dog().TalkToOwner();

        Assert.Equal(expected, actual);
    }

    [Fact]
    public void CatTalkToOwnerReturnsMeow()
    {
        string expected = "Meow!";
        string actual = new Cat().TalkToOwner();

        Assert.Equal(expected, actual);
    }
}

使用 dotnet test 命令重新运行测试,并获得以下输出:

Test run for c:\Users\ronpet\repos\samples\core\console-apps\NewTypesMsBuild\test\NewTypesTests\bin\Debug\netcoreapp2.1\NewTypesTests.dll(.NETCoreApp,Version=v2.1)
Microsoft (R) Test Execution Command Line Tool Version 15.8.0
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...

Total tests: 2. Passed: 2. Failed: 0. Skipped: 0.
Test Run Successful.
Test execution time: 1.6029 Seconds

测试通过。在与所有者谈话时,宠物类型的方法返回正确的值。

你已了解使用 xUnit 来组织和测试项目的方法。继续使用这些方法,将它们应用于自己的项目中。祝你编码愉快!