四、用微软单元测试框架测试微服务

质量保证或测试是以多种方式评估系统、程序或应用的好方法。有时,系统需要对其进行测试以识别错误代码,而在其他情况下,我们可能需要它来评估我们系统的业务合规性。测试可能因系统而异,也可能有很大的不同,这取决于应用的架构风格。一切都取决于我们如何战略性地对待我们的测试计划。例如,测试一个整体.NET 应用不同于测试 SOA 或微服务。

本章的目的是了解测试策略和我们可以使用的不同类型的测试。我们将学习如何在微软单元测试框架和 Moq(一个开源的、友好的嘲讽框架)的帮助下实现单元测试。

在本章中,我们将涵盖以下主题:

  • 测试微服务应用
  • 了解我们的测试策略
  • 测试金字塔
  • 微服务测试的类型
  • 测试微服务应用

技术要求

本章包含各种代码示例,以解释其中的概念。代码将很简单,只是为了演示。

要运行和执行代码,您需要以下先决条件:

  • Visual Studio 2019
  • 。网络核心设置

安装 Visual Studio 2019:

要运行这些代码示例,您需要安装 Visual Studio 2019 或更高版本(首选 IDE)。为此,请遵循以下说明:

  1. https://docs . Microsoft . com/en-us/Visual Studio/install/install-Visual Studio下载 Visual Studio 2019(社区免费)。
  2. 遵循系统的安装说明。Visual Studio 有多个版本。我们使用的是视窗操作系统。

设置.NET Core 3.1:

如果你没有.NET Core 3.1 安装完毕,可以从https://www.microsoft.com/net/download/windows下载。

The complete source code is available at https://github.com/PacktPublishing/Hands-On-Microservices-with-CSharp-8-and-.NET-Core-3-Third-Edition/tree/master/Chapter%2004.

测试微服务应用

测试微服务可能是一项具有挑战性的工作,因为它不同于我们如何测试使用传统架构风格构建的应用。测试一个. NET 单片应用比测试一个微服务稍微容易一点,微服务提供了实现独立性和短的交付周期。

让我们结合我们的.NET 单片应用,我们没有利用持续集成和部署。当测试与持续集成和部署相结合时,它会变得更加复杂。对于微服务,我们需要了解每个服务的测试,以及这些测试之间的区别。另外,请注意,自动化测试并不意味着我们不会执行任何手动测试。

以下是使微服务测试成为一项复杂而具有挑战性的任务的几件事:

  • 对于一个企业系统,微服务可能有多个一起或单独工作的服务,因此它们可能很复杂。
  • 微服务旨在针对多个客户端(但不总是),因此它们涉及更复杂的用例。
  • 微服务架构风格的每个组件/服务都是独立的,所以测试它们有点复杂,因为它们需要单独测试,也需要作为一个完整的系统进行测试。
  • 可能有独立的团队在独立的组件/服务上工作,这些组件/服务可能需要相互交互。因此,测试不仅应该涵盖内部服务,还应该涵盖外部服务。这使得测试微服务的工作更具挑战性和复杂性。
  • 微服务中的每个组件/服务都被设计为独立工作,但是它们可能必须访问公共/共享数据,其中每个服务负责修改自己的数据库。因此,测试微服务将变得更加复杂,因为服务需要使用对其他服务的 API 调用来访问数据,这进一步增加了对其他服务的依赖。这种类型的测试必须使用模拟测试来处理。

本节帮助我们了解如何测试微服务。在下一节中,我们将讨论测试的各种挑战以及如何应对这些挑战。

利用微服务应对测试挑战

在上一节中,我们讨论了测试微服务是一项复杂且具有挑战性的工作。在本节中,我们将讨论一些要点,这些要点将表明如何进行各种测试来帮助我们克服这些挑战。我们现在来看一些:

  • 一个单元测试框架,比如微软单元测试框架,提供了一个工具,我们可以用它来测试独立组件的独立操作。为了确保所有的测试都通过,并且任何新的功能或更改都不会破坏任何东西(如果任何功能破坏了,那么相关的单元测试就会失败),这些测试可以在每次代码编译时运行。
  • 为了确保响应与客户或消费者的期望一致,可以使用消费者驱动的合同测试。
  • 服务使用来自外部方或其他服务的数据,并且可以通过设置负责处理数据的服务端点来测试它们。通过这样做,我们可以使用一个模拟框架或库,比如Moq,在集成过程中模拟这些端点。

在下一节中,我们将研究一些可以帮助我们克服所有这些挑战的测试策略。

理解测试策略

正如我们在第 1 章微服务简介技术要求一节中提到的,部署和质量保证要求可能非常苛刻。有效处理这种情况的唯一方法是先发制人的计划。在早期的需求收集和设计阶段,我一直倾向于让质量保证团队参与进来。在微服务的情况下,有必要在架构组和质量保证组之间进行紧密的协作。质量保证团队的投入不仅会有帮助,而且他们将能够制定策略来有效地测试微服务。

测试策略仅仅是描述完整测试方法的地图或概要计划。不同的系统需要不同的测试方法。不可能实现一个纯粹的测试方法,对于一个使用较新方法开发的系统,而不是较早开发的系统。测试策略应该对每个人都很清楚,这样创建的测试可以帮助团队中的非技术成员(比如涉众)理解系统是如何工作的。这样的测试可以是自动化的,以简单地测试业务流程,或者它们可以是手动测试,这可以由在用户验收测试系统上工作的用户来执行。

测试策略或方法有以下技术:

  • 主动:这是一个早期的方法,它试图在从初始测试设计创建构建之前修复缺陷。
  • 反应式:在这种方法中,一旦编码完成,测试就开始了。

在本节中,我们研究了可用于测试的策略,并讨论了主动和被动测试方法。在下一节中,我们将看看测试金字塔以及测试方法是如何流动的。

测试金字塔

测试金字塔是一种策略或方法,用来定义应该在微服务中测试什么。换句话说,我们可以说它帮助我们定义了微服务的测试范围。测试金字塔的概念是由 Mike Cohn 在 2009 年创建的。测试金字塔有多种口味;不同的作者描述了这一点,指出了他们是如何放置测试范围或确定测试范围的优先级的。下图描述了迈克·科恩定义的概念:

测试金字塔展示了设计良好的测试策略是如何构建的。当我们仔细观察它时,我们可以很容易地看到我们应该如何遵循微服务的测试方法(请注意,测试金字塔并不特定于微服务)。让我们从金字塔的底部开始。我们可以看到测试范围仅限于使用单元测试。一旦我们移动到顶部,我们的测试范围就扩展到更广的范围,在那里我们可以执行完整的系统测试。

让我们详细讨论这些层(采用自下而上的方法):

  • 单元测试:这些测试基于微服务的架构风格,测试应用的小功能。
  • 服务测试:这些是测试独立服务或与另一个/外部服务通信的服务的测试。
  • 系统测试:这些测试有助于测试整个系统,包括用户界面的一个方面。这些是端到端的测试。

有趣的一点是,顶层测试,也就是系统测试,编写和维护起来既慢又昂贵。另一方面,底层测试,也就是单元测试,速度相对较快,成本也较低。

测试金字塔是关于可以在应用的不同层上完成的各种测试。在本节中,我们按照测试金字塔讨论了单元、服务和系统测试。在下一节中,我们将详细讨论这些测试,并将涵盖各种类型的微服务测试。

微服务测试的类型

在前一节中,我们讨论了测试方法或测试策略。这些策略帮助我们决定如何继续测试系统。测试策略为我们提供了各种类型的测试,可以帮助我们测试整个系统。在本节中,我们将讨论各种类型的微服务测试。

单元测试

单元测试通常测试单个函数调用,以确保测试程序的最小部分。这些测试旨在验证特定功能,而不考虑其他组件。

以下是帮助验证特定功能的各种组件:

  • 当组件被分解成小的、独立的、应该被独立测试的部分时,测试将变得更加复杂。在这里,测试策略派上了用场,它们确保系统的最佳质量保证得以实现。当涉及到测试驱动开发 ( TDD )方法时,我们增加了更多的动力。我们将借助单元测试部分中的一个例子来讨论这一点,该部分是实际测试部分的一个小节。

You can learn and practice TDD, with the help of Katas at https://github.com/garora/TDD-Katas.

  • 单元测试可以是任何规模的;单元测试没有明确的规模。一般来说,这些测试是在类级别编写的。
  • 较小的单元测试有利于测试复杂系统的每一个可能的功能。

组件(服务)测试

组件测试(或服务测试)是一种绕过用户界面直接测试应用编程接口(在我们的例子中,是 ASP.NET Core 网络应用编程接口)的方法。使用这个测试,我们确认一个单独的服务没有任何代码错误,并且它在功能方面工作正常。

测试服务并不意味着它是一个独立的服务。此服务可能正在与外部服务交互。在这种情况下,我们不应该调用实际的服务,而是使用模拟和存根方法。这样做的原因在我们的座右铭中:测试代码并确保它没有错误。在我们的例子中,我们将使用moq框架来模拟我们的服务。

谈到组件或服务测试,有几件事值得注意:

  • 因为我们需要验证服务的功能,所以这类测试应该是小而快速的。
  • 借助嘲讽,我们不需要处理实际的数据库;因此,测试执行时间更少或名义上更长。
  • 这些测试的范围比单元测试更广泛。

集成测试

在单元测试中,我们测试单个代码单元。在组件或服务测试中,我们通过依赖外部或第三方组件来测试模拟服务。然而,微服务中的集成测试可能有点挑战性,因为我们测试协同工作的组件。在这里,应该进行服务调用,以便它们与外部服务集成。在这个测试策略中,我们确保系统正常工作,并且服务按预期运行。在我们的例子中,我们有各种各样的微服务,其中一些依赖于外部服务。

例如,库存服务依赖于订单服务,因为一旦客户成功订购了特定的商品,就会从库存中减少特定数量的商品。在这个场景中,当我们测试 StockService 时,我们应该模拟 OrderService。我们的目标应该是测试 StockService,而不是与 OrderService 通信。我们不直接测试任何服务的数据库。

合同测试

合同测试是一种方法,其中每个服务调用都独立地验证响应。如果有任何服务是依赖的,那么依赖关系就会被存根化。这样,服务在不与任何其他服务交互的情况下运行。这是一个集成测试,允许我们检查外部服务的合同。这遵循了一个称为消费者驱动契约的概念(我们将在下一节详细讨论这一点)。

例如,CustomerService允许新客户在 FlixOne 商店注册。我们不会在数据库中存储新客户的数据。我们首先验证客户数据,以检查黑名单或欺诈用户列表,等等。这个过程通过调用由另一个团队或完全由第三方维护的外部服务来实现。如果有人更改了这个外部服务的合同,我们的测试仍然会通过,因为这样的更改不应该影响我们的测试,因为我们取消了这个外部服务的合同。

微服务可以有几个独立的服务,它们可以相互通信,也可以不相互通信。为了测试这些服务,我们应该有某种模式或机制来确保和验证交互。为此,我们可以使用消费者驱动的合同。

消费者驱动的合同

在微服务中,我们有几个独立的服务,或者需要相互通信的服务。除此之外,从用户的角度来看(这里,用户是一个开发人员,他正在使用被引用的应用编程接口),他们知道服务,以及它是否有几个客户端/消费者/用户。这些客户可以有相同或不同的需求。

消费者驱动的契约指的是一种模式,它指定并验证客户端/消费者和 API 所有者(应用)之间的所有交互。在这里,消费者驱动意味着客户/消费者用定义的格式指定它所要求的交互类型。另一方面,应用接口所有者(应用服务)必须同意这些合同,并确保它们不会违反这些合同。下图是消费者驱动合同的示意图,其中合同介于消费者和其提供商之间:

这些是合同:

  • 提供商合同:这只是对 API 所有者(应用)提供的服务的完整描述。斯瓦格的文档可以用于我们的 REST API(网络 API)。
  • 消费者合同:这是对消费者/客户将如何利用提供商合同的描述。
  • 消费者驱动合同:这是对 API 所有者如何满足消费者/客户合同的描述。

如何实现消费者驱动的测试

在微服务的情况下,实现消费者驱动的测试比. NET 单片应用更具挑战性。这是因为,在单片应用中,我们可以直接使用任何单元测试框架,比如 MS tests 或者 NUnit,但是我们不能在微服务架构中直接这样做。在微服务中,我们不仅需要模拟方法调用,还需要模拟通过 HTTP 或 HTTPS 调用的服务本身。

为了实现消费者驱动的测试,我们可以使用各种工具。一个著名的开源工具.NET frameworks 是PactNet(https://github.com/SEEK-Jobs/pact-net,而另一个为。网芯是Pact.Net 芯(https://github.com/garora/pact-net-core)。这些是基于协定(https://docs.pact.io/标准。在本章的最后,我们将研究消费者驱动的契约测试。

契约核心如何帮助我们实现目标

在消费者驱动的测试中,我们的目标是确保我们能够测试依赖于其他/外部服务或与之通信的所有服务和内部组件。

Pact-net-core 的编写方式保证了合同的履行。以下是它如何帮助我们实现目标的几点:

  • 执行速度非常快。
  • 它有助于确定故障原因。
  • Pact 不需要单独的环境来管理自动化测试集成。

我们需要遵循两个步骤来与 Pact 合作:

  1. 定义期望:这里,消费者团队要定义合同。在下图中,Pact 帮助记录消费者合同,该合同将在重放时得到验证:

  1. 验证期望:作为下一步的一部分,将合同交给提供商团队,然后实施提供商服务来履行同样的义务。在下图中,我们展示了如何在提供者端重放契约,以实现定义的契约:

在这一节中,我们研究了消费者驱动的合同,这有助于我们在名为 Pact-net 的开源工具的帮助下减轻微服务架构的挑战。在消费者驱动的契约的帮助下,我们可以确保我们可以在没有来自服务的实际输入的情况下工作。为了了解它是如何工作的,我们将研究性能测试。

性能试验

这是非功能测试的一种形式,它的主要座右铭是不验证代码或测试代码的健康状况。这意味着根据各种度量,即可伸缩性、可靠性等,确保系统运行良好。

以下是不同的技术或性能测试类型,它们帮助我们在各个方面测试系统,以便我们可以测试其性能:

  • 负载测试:这是我们在特定负载的各种情况下测试系统行为的过程。这还包括关键事务、数据库负载、应用服务器等。
  • 压力测试:这是一种对系统进行回归测试,找出系统容量上限的方法。它还决定了系统在当前负载高于预期最大负载的情况下的行为。
  • 浸泡测试:这也叫耐力测试。在此测试中,主要目的是监视内存利用率、内存泄漏或影响系统性能的各种因素。
  • 尖峰测试:这是一种确保系统能够承受工作负载的方法。确定性能的最佳任务之一是突然增加用户负载。

端到端(用户界面/功能)测试

端到端、用户界面或功能测试是为整个系统执行的测试,包括整个服务和数据库。这些测试增加了测试的范围。这是最高级别的测试,它包括前端集成,并以终端用户使用系统的方式测试系统。这个测试类似于最终用户在系统上的工作方式。

社交单元测试和孤立单元测试

社交单元测试是那些包含具体合作者和跨边界的测试。它们不是孤立的测试。单独测试确保一个类的方法被测试。社交测试并不新鲜。马丁·福勒将这个概念详细解释为一个单元测试(https://martinfowler.com/bliki/UnitTest.html)。让我们更详细地看看这两个:

  • 社交测试:这是一个让我们知道应用正在按预期工作的测试。这是一个环境,在这个环境中,其他应用可以正常运行,并产生预期的结果。它还针对相同的环境测试新功能/方法的功能,包括其他软件。社交测试类似于系统测试,因为这些测试的行为类似于系统测试。
  • 孤立的单元测试:顾名思义,你可以使用这些测试以孤立的方式执行单元测试,通过执行存根和嘲讽。我们可以使用存根用一个具体的类来执行单元测试。

单元测试是确保应用代码得到良好测试的一种方式;通过编写代码来测试代码。单元测试是由开发人员编写的,因此这些测试可以确保代码正常工作。通过调整这些测试,我们可以确保我们的应用在特定的环境中运行良好。

本节概述了在某些环境中,我们可以用来验证类方法和应用功能的不同测试。但是,我们需要在测试期间使用数据时了解流的测试功能,因为我们不建议您使用实际数据。正因为如此,我们需要了解如何制作模型。在下一节中,我们将讨论存根和模拟。

存根和模拟

有时,我们需要测试有无数据的流程。在我们需要进行测试的情况下,我们不能使用实际数据,因为不建议在测试期间使用实际数据(一个原因是您不想更改数据)。这同样适用于我们不使用数据的测试;在这种情况下,我们需要验证输出。为了进行这些测试,我们需要一种安排,在这种安排中,我们可以创建一个具有一些标准输出的框架,而不依赖于正在测试的方法的输入。这可以通过存根和模拟来完成,其中存根是表示响应/结果或输出的对象,但是输入中的任何更改都不会影响存根。另一方面,模拟对象是包含被测试方法的预期输出的假对象。

存根是测试过程中对调用的固定响应;嘲笑意在设定期望。请参见以下描述:

  • 存根:存根对象不依赖于接收到的输入,这意味着响应或结果不会因正确或不正确的输入而受阻。最初,我们创建旨在成为对象的存根。
  • 模仿:模仿的对象不是真实的,可能永远都是假的。使用这个,您可以测试可以调用的方法,并告诉我们单元测试是失败了还是通过了。换句话说,我们可以说模拟对象只是我们实际对象的复制品。在下面的代码中,我们使用moq框架来实现一个模拟对象:
 [Fact]
 public void Get_Returns_ActionResults()
 {
    // Arrange
    var mockRepo = new Mock<IProductRepository>();
    mockRepo.Setup(repo => repo.GetAll().
    ToViewModel()).Returns(GetProducts());
    var controller = new ProductController(mockRepo.Object);
    // Act
    var result = controller.Get();
    // Assert
    var viewResult = Assert.IsType<OkObjectResult>(result);
    var model = Assert.IsAssignableFrom<
    IEnumerable<ProductViewModel>>(viewResult.Value);
    Assert.Equal(2, model.Count());
 }

在前面的代码示例中,我们模拟了我们的存储库IProductRepository,并且验证了模拟的结果。

本节的目的是涵盖我们可以用于基于微服务的应用的各种测试。在下一节中,我们将通过使用 FlixOne 书店应用中的更多代码示例来更详细地理解这些术语。

测试微服务应用

到目前为止,我们已经讨论了测试策略和各种类型的微服务测试。我们还讨论了如何测试以及测试什么。在本节中,我们将看到一些测试正在进行。我们将按照以下最低要求实施这些测试:

  • Visual Studio 2019
  • 。网络核心 3.1
  • C# 8.0
  • 迅驰和微软测试
  • 混合氧化物框架

为测试项目做准备

首先,我们将测试我们的微服务应用 FlixOne 书店。在一些代码示例的帮助下,我们将学习如何执行单元测试、存根和嘲讽。

We created the FlixOne bookstore application in Chapter 2, Refactoring the Monolith.

在我们开始编写测试之前,我们应该在现有的应用中建立一个测试项目。对此,我们可以遵循几个简单的步骤:

  1. 从解决方案资源管理器的“使用 Visual Studio”中,右键单击解决方案,然后单击添加|新建项目,如下图所示:

  1. 从添加新项目模板中,搜索.NET Core 并选择 xUnit 测试项目(.NET Core):

  1. 提供一个有意义的名称,例如FlixOne.BookStore.ProductService.UnitTests:

  1. 我们项目的默认语言版本是 C# 8.0(参考第二章重构整块,了解更多关于选择语言版本的信息)。

我们的项目结构应该如下所示:

前面的截图展示了我们测试项目的结构,也就是 flixone . Booker . productservice . unittests,它有两个文件夹:Fake 和 Services。ProductData.cs 文件包含假数据,属于“假”文件夹。另一方面,ProductTest.cs 文件属于服务文件夹,包含单元测试。

单元测试

让我们测试一下ProductService,确保我们的服务返回产品数据没有失败。在这里,我们将使用假物体。请遵循以下步骤:

  1. 从测试项目中删除默认的UniTest.cs文件。
  2. 添加一个新文件夹,然后在FlixOne.BookStore.ProductService.UnitTests项目中将其命名为Fake
  3. Fake文件夹下,添加ProductData.cs类,然后添加如下代码:
 public IEnumerable<ProductViewModel> GetProducts()
  {
    var productVm = new List<ProductViewModel>
    {
      new ProductViewModel
      {
        CategoryId = Guid.NewGuid(),
        CategoryDescription = "Category Description",
        CategoryName = "Category Name",
        ProductDescription = "Product Description",
        ProductId = Guid.NewGuid(),
        ProductImage = "Image full path",
        ProductName = "Product Name",
        ProductPrice = 112M
      },
      new ProductViewModel
      {
        CategoryId = Guid.NewGuid(),
        CategoryDescription = "Category Description-01",
        CategoryName = "Category Name-01",
        ProductDescription = "Product Description-01",
        ProductId = Guid.NewGuid(),
        ProductImage = "Image full path",
        ProductName = "Product Name-01",
        ProductPrice = 12M
      }
    };
    return productVm;
  }

前面的代码片段正在创建ProductViewModel类型的伪数据。

在下面的代码块中,我们通过制作ProductViewModelProduct两个列表来创建假数据:

  public IEnumerable<Product> GetProductList()
  {
    return new List<Product>
    {
      new Product
      {
        Category = new Category(),
        CategoryId = Guid.NewGuid(),
        Description = "Product Description-01",
        Id = Guid.NewGuid(),
        Image = "image full path",
        Name = "Product Name-01",
        Price = 12M
      },
      new Product
      {
        Category = new Category(),
        CategoryId = Guid.NewGuid(),
        Description = "Product Description-02",
        Id = Guid.NewGuid(),
        Image = "image full path",
        Name = "Product Name-02",
        Price = 125M
      }
    };
  }

前面的代码显示了两个列表。这些列表包含假数据来测试ProductViewModelProduct的输出。

  1. Services文件夹添加到FlixOne.BookStore.ProductService.UnitTests项目中。
  2. Services文件夹下,添加ProductTests.cs类。
  3. 打开 NuGet 管理器,然后搜索添加moq,如下图截图所示:

  1. 将以下代码添加到ProductTests.cs类中:
public class ProductTests
{
    [Fact]
    public void Get_Returns_ActionResults()
    {
      // Arrange
      var mockRepo = new Mock<IProductRepository>();
      mockRepo.Setup(repo => repo.GetAll()).
      Returns(new ProductData().GetProductList());
      var controller = new ProductController(mockRepo.Object);
      // Act
      var result = controller.GetList();
      // Assert
      var viewResult = Assert.IsType<OkObjectResult>(result);
      var model = Assert.IsAssignableFrom<IEnumerable<
      ProductViewModel>>(viewResult.Value);
      Assert.NotNull(model);
      Assert.Equal(2, model.Count());
    }
}

在前面的代码中,这是一个单元测试示例,我们正在模仿我们的存储库,并测试我们的网络应用编程接口控制器的输出。本测试基于 AAA 技术;如果您在设置过程中遇到模拟数据,它将被通过。

在本节中,我们通过对ProductService执行单元测试来介绍单元测试,并通过使用假对象来验证其方法输出。现在,我们需要学习如何测试集成一个或两个模块/块的方法或代码块。为此,我们需要了解集成测试。

集成测试

ProductService中,让我们确保我们的服务返回产品数据没有失败。在我们继续之前,我们必须添加一个新项目和后续的测试类。请按照以下步骤操作:

  1. 右键单击解决方案,然后单击添加项目。

  2. 从“添加新项目”窗口中,选择“测试项目”(.NET Core),如下图截图所示:

  1. 提供一个有意义的名字;例如FlixOne.BookStore.ProductService.IntegrationTests:

  1. 添加appsettings.json文件,然后添加以下代码:
 {
   "ConnectionStrings": 
   {
     "ProductConnection": "Data Source=.;Initial
     Catalog=ProductsDB;Integrated
     Security=True;MultipleActiveResultSets=True"
   },
   "buildOptions": 
   {
     "copyToOutput": 
     {
       "include": [ "appsettings.json" ]
     }
   }
 } 
  1. 打开FlixOne.BookStore.ProductService项目的Startup.cs文件。

  2. 现在,使ConfigureServicesConfigure方法无效。我们这样做是为了在我们的TestStartup.cs类中覆盖这些方法。这些方法如下所示:

public virtual void ConfigureServices(IServiceCollection services)
{
   services.AddTransient<IProductRepository,
   ProductRepository>();
   services.AddDbContext<ProductContext>(
   o => o.UseSqlServer(Configuration.
   GetConnectionString("ProductConnection")));
   services.AddMvc();
   //Code ommited
}
public virtual void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
 if (env.IsDevelopment())
 {
 app.UseDeveloperExceptionPage();
 app.UseBrowserLink();
 }
 else
 {
 app.UseExceptionHandler("/Home/Error");
 }
 app.UseStaticFiles();
 app.UseMvc(routes =>
 {
 routes.MapRoute(name: "default",
 template: "{controller=Home}/{action=
 Index}/{id?}");
 });
 app.UseSwaggerUI(op =>
 {
 op.SwaggerEndpoint("/swagger/v1/swagger.json", 
 "Product API V1");
 });
}
  1. 添加一个名为Services的新文件夹。
  2. 增加TestStartup.cs类。
  3. 打开 NuGet 管理器。搜索并添加微软。AspNetCore.TestHost 包,如下图截图所示:

  1. TestStartup.cs中添加以下代码:
public class TestStartup : Startup
{
   public TestStartup(IConfiguration 
   configuration) : base(configuration)
   { }
   public override void ConfigureServices
   (IServiceCollection services)
   {
   //mock context
   services.AddDbContext<ProductContext>(
   o => o.UseSqlServer(Configuration.
   GetConnectionString("ProductConnection")));
   services.AddMvc();
   }
 public override void Configure(IApplicationBuilder
 app, IHostingEnvironment env)
   { }
}
  1. Services文件夹下,添加一个新的ProductTest.cs类,然后在其中添加以下代码:
public class ProductTest
{
  public ProductTest()
  {
    // Arrange
    var webHostBuilder = new WebHostBuilder()
    .UseStartup<TestStartup>();
    var server = new TestServer(webHostBuilder);
    _client = server.CreateClient();
  }
  private readonly HttpClient _client;
  [Fact]
  public async Task ReturnProductList()
  {
    // Act
    var response = await _client.GetAsync
    ("api/product/productlist"); //change per //setting
    response.EnsureSuccessStatusCode();
    var responseString = await response.Content.
    ReadAsStringAsync();
    // Assert
    Assert.NotEmpty(responseString);
  }
}
  1. 要验证这些测试,请右键单击解决方案,然后选择“运行测试”(如果您面临 NuGet 包错误,请确保恢复所有包)。

在前面的代码示例中,我们正在检查一个简单的测试。我们试图通过使用HttpClient设置一个客户端来验证服务的响应。如果响应为空,测试将失败。

摘要

测试微服务与测试以传统架构风格构建的应用有点不同。在. NET 单片应用中,与微服务相比,测试稍微容易一些,并且它提供了实现独立性和短的交付周期。微服务在测试时面临挑战。

借助测试金字塔概念,我们可以制定测试程序的策略。就测试金字塔而言,我们可以很容易地看到,单元测试允许我们测试一个类的一个小函数,并且耗时更少。另一方面,测试金字塔的顶层范围很大,有系统测试或端到端测试,这些测试既耗时又非常昂贵。消费者驱动的契约是测试微服务的非常有用的方法。Pact-net 是一个用于此目的的开源工具。最后,我们进行了实际的测试实现。

在下一章中,我们将学习如何使用 Docker 部署微服务应用。我们将详细讨论延续集成和延续部署。

问题

  1. 什么是单元测试?
  2. 开发人员为什么要坚持测试驱动开发?
  3. 什么是存根和模拟对象?
  4. 什么是测试金字塔?
  5. 什么是消费者测试?
  6. 我们如何在基于微服务的应用中使用消费者测试?

进一步阅读

以下是一些参考资料,将增强您的测试知识: