六、测试 RESTful Web 服务

一个系统只有经过各种场景的测试才能成熟。 这些场景通常基于领域专家或现有生产环境的经验。 在生产环境中,系统总是有崩溃的可能,即使系统被称为完美系统。 对于 web 应用,由于性能故障、糟糕的用户体验等原因,这些条件甚至更为关键。 一个系统应该经过一个过程或一系列的发展原则来处理这类问题。 简单地说,我们必须测试这个系统。 测试是一个确保系统质量的过程。

换句话说,质量保证,或测试,是一种从不同方面评估系统的方法。 当系统需要测试以识别错误代码时,或者当我们希望评估其业务遵从性时,这个过程也很有用。

质量保证是对一个系统进行评估并确保其质量的过程。

测试完全依赖于系统的架构风格,它因系统而异; 一切都取决于我们如何为我们的测试方法或计划制定策略。

在本章中,我们将主要关注测试 RESTful 服务,并通过遵循测试驱动的开发方法使我们的代码更好。 在本章的最后,您将能够在日常开发活动中使用测试范例,了解存根,模拟对集成和安全性以及性能测试的理解。

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

  • 测试范例(质量保证的基础,包括测试用例的创建)
  • 测试 ASP.NET Core 控制器(单元测试)
  • 存根和嘲笑
  • 安全性测试
  • 集成测试
  • 假的物品
  • 使用 Postman、Advanced RESTClient 等测试服务调用
  • 用户验收测试
  • 性能或负载测试

测试模式

在前面的部分中,我们看到测试和质量保证是软件开发周期中最重要的部分之一。 我们应该采取步骤设计一个测试软件的框架,这个框架被称为测试范例

测试范式是测试的框架。 它基于一个人计划实现测试的方式。 简而言之,测试范例是一种测试方法。

测试方法是您决定如何创建测试用例的地方,包括它的语言是什么,您将如何记录测试用例,等等。 这还告诉您将如何执行测试方法(例如,使用黑盒测试)。

测试方法是在特定输入的基础上测试或验证特定输出的一种方法,而不需要知道系统的内部功能。

在我们创建测试用例或开发测试范例或测试框架之前,我们需要掌握一些重要的术语。

测试覆盖率和代码覆盖率

一般来说,覆盖率是指被覆盖的内容以及如何衡量覆盖率。 从开发人员的角度来看,在测试驱动开发中编写单元测试可以告诉我们如何以及覆盖哪些代码区域。

测试期间执行的代码的度量是代码覆盖率。 测试期间执行的测试用例的度量是测试覆盖率。

对代码进行了单元测试,并证明所覆盖的代码也经过了测试。 在这个代码覆盖率中,会涉及到很多东西,即代码行、函数、条件、表达式、API 资源等等。

软件测试术语请参见http://castb.org/wp-content/uploads/2014/05/istqb_glossary_of_testing_terms_v2.3.pdf

测试覆盖率和代码覆盖率也可以覆盖以下任何测试类型:

  • 单元测试
  • 安全性测试
  • 集成测试

在接下来的部分中,我们将使用代码示例详细查看这些测试。

任务、场景和用例

当某人使用一个测试范例时,他们应该知道任务、场景和用例。 在本节中,我们将详细讨论这些术语:

  • Task:Task 是一个通用词,不仅与软件行业相关,而且也与许多其他行业相关。 这是一个需要完成的行为或工作。 完成任务有不同的方法,但任务的总体意图是它应该被完成。 在不同的领域,任务有不同的目的。 在 scrum 开发(https://whatis.techtarget.com/definition/storyboard)中,故事板或任务板帮助开发人员理解需要完成的工作。

下面的图表说明了任务的含义:

前面的图表是一个故事或任务板; 它显示了完成一本书所需的各种任务,从数据收集到技术审查。 市场上有很多免费或付费的工具来管理这些类型的任务。

  • 场景:通常情况下,场景就是系统在与客户交互后发生故障的情况。 换句话说,场景是一种理解和详细编写步骤的方式。 例如,有一些情况可能导致系统的登录功能失败,这些情况将作为场景进行记录。 在软件测试中,场景也被称为测试场景。 一个场景通常导致一个或多个测试。
  • 用例:用例是系统和用户之间可能的交互序列的集合。 它还可以是系统实现时应该评估的可能场景的集合。 这些用例更详细和记录,并被划分为不同的步骤,如下图所示:

在前面的图中,很明显,测试用例的子集是测试场景,用例,测试场景的超集。 无论何时您创建一个测试用例,它都来自于一个测试场景。****

****# 检查表

一般来说,清单只是一个项目列表,其中需要一个行动来实现一个目标。 清单可以是待办事项列表、日常活动列表或开发人员任务列表。

在测试领域中,列表可以包含要验证的测试用例、需要执行的测试列表,等等。 清单因人而异,开发人员因人而异,甚至组织与组织也各不相同,但清单的目的总是限制遗忘某些东西的人类行为。

漏洞和缺陷

术语 bug 和缺陷是行业中最常用的术语。 在一些组织中,这些术语可以互换使用。 但是,通常情况下,错误是与一些正确完成但执行了意外输出的事情相关的,例如,2 + 3 = 6。 另一方面,缺陷是在计划期间被遗漏的东西。

关于 bug 和缺陷需要注意的一些事情:

  • 错误几乎总是由于需求的不纯实现,例如,错误地满足基本需求的代码
  • bug 通常是在开发或测试阶段确定的
  • 缺陷是与在生产过程中遗漏了客户或客户的设计或需求差距相关的
  • 缺陷往往表明人为错误
  • bug 可以在测试期间被发现时修复
  • 缺陷会导致系统出现故障,从而导致设计问题

测试方法

通常,测试方法是说明如何执行测试的执行路径。 这些方法因系统而异; 如果一个系统需要协商方法,并不意味着另一个系统也需要协商方法。 不同的系统需要不同的测试方法。

测试方法是一种测试策略,它只是一个系统或项目的实现。

每个人都应该清楚地了解测试策略,以便创建的测试可以帮助团队的非技术成员(如涉众)理解系统是如何工作的。 这样的测试可以是自动化的,例如测试业务流,也可以是手动测试,由用户在用户验收测试系统上执行。

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

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

测试金字塔

测试金字塔是一种策略或一种方法,用来定义在 RESTful 服务中应该测试什么。 换句话说,我们可以说测试金字塔帮助我们定义 RESTful 服务的测试范围。

The concept of the testing pyramid was developed by Mike Cohn (http://www.mountaingoatsoftware.com/blog/the-forgotten-layer-of-the-test-automation-pyramid) in 2009.

有各种各样的测试金字塔; 不同的作者通过指示他们如何放置或优先化他们的测试范围来描述这一点。

下图描述了与 Mike Cohn 定义的相同的概念:

让我们详细讨论一下这些层。

  • 单元测试:单元测试以 ASP 开发的 RESTful 服务应用的单元测试小功能.NET Core
  • RESTful 服务测试(验收测试):这些测试用于测试独立服务或与另一个(通常是外部)服务通信的服务
  • GUI 测试(REST 客户端测试):这些测试属于将使用 RESTful 服务的客户端或使用者; 它们有助于用用户界面的某个方面测试整个系统,并且是端到端测试

我们将讨论关于用 ASP 开发的 RESTful 服务的应用的测试。 净的核心。

类型的测试

在前面的部分中,我们讨论了测试方法或测试策略。 这些策略决定了我们将如何对系统进行测试。 在本节中,我们将讨论应用中使用的各种类型的测试。

测试 ASP。 核心控制器(单元测试)

单元测试通常测试单个函数调用,以确保测试程序的最小部分。 因此,这些测试旨在验证特定的功能,而不考虑其他组件。 在这里,测试策略可以派上用场,并确保系统的最佳质量保证将得到执行。 当使用测试驱动开发(TDD)方法时,它增加了更多的功能。

您可以在https://github.com/garora/TDD-Katas的 Katas 帮助下学习和实践 TDD。

我们将通过一个代码示例来讨论这个问题。 在我们继续之前,请先看看以下先决条件:

  • Visual Studio 2017 Update 3 或更高版本
  • .NET Core 2.0 或更高版本
  • c# 7.0 或更高版本
  • ASP.NET Core 2.0 或更高版本
  • 实体框架 Core 2.0 或更高版本
  • xUnit 和 MS 测试
  • moq 框架

为考试做准备

在本节中,我们将创建一个 ASP.NET Core API,然后进行单元测试。

完成以下步骤创建您的应用:

  1. 打开 Visual Studio。
  2. 转到文件|新建|项目或按Ctrl+Shift+F5:

  1. 选择 ASP.NET Core Web 应用。
  2. 从模板窗口中,选择 ASP.NET Core api——确保你选择了。NET Core 2.0。
  3. 命名项目,选择解决方案的路径,然后单击 OK。
  4. 在 Solution Explore 中添加Core文件夹,右键单击并选择 Add New Folder,并将其命名为Model
  5. Core文件夹下添加InterfacesModel文件夹。
  6. Model文件夹下添加一个新类-在解决方案资源管理器中右键单击Model文件夹并选择 Add new Item。 然后,选择Class或按Shift+Alt+c

请注意,快捷键根据您对 Visual Studio 的设置而不同。

  1. 将其命名为Product.cs,并将以下代码添加到该类中:
namespace Chap06_01.Core.Model
{
  public class Product
  {
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string Image { get; set; }
    public decimal Price { get; set; }
    public Guid CategoryId { get; set; }
    public virtual Category Category { get; set; }
  }
}
  1. 重复步骤 78加入Category.csProductViewModel.cs
  2. 重复步骤 6,添加Infrastructure文件夹。
  3. 下添加一个新类InfrastructureInfrastructurefolder-right-click 文件夹在解决方案资源管理器中,选择添加新项目,和,选择类或单击转变+Alt + c * 命名为ProductContext.cs

*在这个演示项目中,我们没有遵循测试驱动的开发方法; 为了演示目的,我们将对应用进行单元测试。

  1. 现在,打开appsettings.json文件并添加以下代码片段:
"ConnectionStrings": 
{
  "ProductConnection": "Data Source=.;Initial
  Catalog=ProductsDB;Integrated
  Security=True;MultipleActiveResultSets=True"
}
  1. 在解决方案资源管理器中右键单击项目并选择管理 Nuget 包。
  2. 在 Nuget Package Manager 屏幕下,搜索Swashbuckle.AspNetCore并安装它。

Swagger is open source and adheres to open specifications (https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md). Swagger allows you to describe an API's structure. Swagger provides documentation to users (devs who are going to use APIs). There are a lot of open source and commercial tools available that can integrate with Swagger.  Swagger CodeGen (https://swagger.io/swagger-codegen/) helps to generate client libraries for an API.

Swagger UI (https://swagger.io/swagger-ui/) helps to generate an API's documentation.

Swashbuckle.AspNetCore (https://github.com/domaindrivendev/Swashbuckle.AspNetCore) is a tool that helps document APIs built on ASP.NET Core.

  1. Core/Interfaces下面加上interface IProductRepository
  2. IProductRepository接口中添加以下代码:
namespace Chap06_01.Core.Interfaces
{
  public interface IProductRepository
  {
    void Add(Product product);
    IEnumerable<Product> GetAll();
    Product GetBy(Guid id);
    void Remove(Guid id);
    void Update(Product product);
  }
}

Please note that for the complete source code, refer to the GitHub repository at https://github.com/PacktPublishing/Building-RESTful-Web-Services-with-DotNET-Core.

  1. Infrastructure文件夹下添加ProductRepository类。
  2. 将以下代码添加到ProductRepository:
namespace Chap06_01.Infrastructure
{
  public class ProductRepository : IProductRepository
  {
    private readonly ProductContext _context;
    public ProductRepository(ProductContext context)
    => _context = context;
    public IEnumerable<Product> GetAll() =>
    _context.Products.Include(c => 
    c.Category).ToList();
    public Product GetBy(Guid id) => _context.Products.
    Include(c => c.Category).FirstOrDefault(x => x.Id == id);
    public void Add(Product product)
    {
      _context.Products.Add(product);
      _context.SaveChanges();
    }
    public void Update(Product product)
    {
      _context.Update(product);
      _context.SaveChanges();
    }
    public void Remove(Guid id)
    {
      var product = GetBy(id);
      _context.Remove(product);
      _context.SaveChanges();
    }
  }
}
  1. 打开Startup.cs文件,添加以下代码:
services.AddScoped<IProductRepository, ProductRepository>();
services.AddDbContext<ProductContext>
(
  o => o.UseSqlServer(Configuration.GetConnectionString
  ("ProductConnection"))
);
services.AddSwaggerGen
(
  swagger =>
  {
    swagger.SwaggerDoc("v1", new Info { Title = "Product 
    APIs", Version = "v1" });
  }
);

你的项目层次结构现在看起来应该像下面的解决方案资源管理器的截图:

现在,您可以开始使用该应用了! 从菜单中运行应用或按F5。 在浏览器中,在 URL 地址栏添加后缀/swagger,如下图所示:

这个 URL 应该显示 swagger API 文档,如下面的截图所示:

如果你点击 GET /api/Product/productlist,它应该会返回一个产品列表,如下图所示:

编写单元测试

在本节中,我们将使用 ASP 添加一个测试项目.NET Core 2.0 和使用 xUnit 编写单元测试。 在开始编写测试之前,我们应该在现有的应用中设置一个测试项目。

下面是我们的测试项目设置所需的几个简单步骤:

  1. 从 Visual Studio 的解决方案资源管理器,右键单击解决方案'Chap06_01'(1 项目),并单击添加|新项目… ,如下图所示:

  1. 从 Add New Project 模板中,选择. net Core 和 xUnit Test Project(.NET Core),并提供一个有意义的名称,例如Chap06_01_Test:

  1. 添加名为FakeServices的文件夹。 (请参阅前一节,了解如何从解决方案资源管理器中添加新文件夹。) 你的项目结构现在看起来应该像下面的截图:

  1. ProductData.cs类应该如下所示:
namespace Chap06_01_Test.Fake
{
  public class ProductData
  {
    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;
    }
    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
        }
      };
    }
  }
}

在前面的代码片段中,我们为ProductsProductsViewModel创建了假数据。

The full code is available to download from https://github.com/PacktPublishing/Building-RESTful-Web-Services-with-DotNET-Core.

  1. 我们的单元测试类ProductTest.cs看起来如下所示:

Important terms for xUnit:

  • Fact是一个属性,用于不带参数的正常测试方法
  • 理论是一个属性,用于参数化测试方法
namespace Chap06_01_Test.Services
{
  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());
    }
  }
}

在前面的代码片段中,我们只是测试我们的ProductController,它是一个Get资源GetList。 在这段代码中,我们对列表进行了模拟; 我们实际上并没有攻击数据库,而是使用假数据测试我们的Controller方法。

  1. 从测试资源管理器运行测试; 如果你的测试通过了,你会看到如下截图:

存根和嘲笑

存根是对测试期间进行的调用的响应,而 mock 旨在设置期望。 它们可以进一步解释如下:

  • 存根:在stubs对象中,我们总是得到一个有效的存根响应。 响应并不关心您提供了什么输入。 在任何情况下,输出都是相同的。
  • mock:在mock对象中,我们可以测试或验证可以在被 mock 对象上调用的方法。 这是一个伪对象,用于验证单元测试是失败还是通过。 换句话说,我们可以说模拟对象只是实际对象的副本。

在前一节编写单元测试中,我们使用最小起订量框架来实现模拟对象。

安全性测试

安全性是一个非常广泛的术语,不能用几行字来解释。 一般来说,安全测试是一种测试应用是否安全或是否有可能泄露某人的数据的方法。

安全性和安全系统将在第八章安全 RESTful Web 服务中讨论。

安全测试非常重要,尤其是当我们在基于 web 的应用中工作时。 Web 应用是公开的,容易受到攻击,因此身份验证和授权是这里最重要的因素。

FxCop (https://en.wikipedia.org/wiki/FxCop), which is shipped with Visual Studio and VeraCode (https://www.veracode.com/), is one of the most popular tools used in security testing.

集成测试

在单元测试中,我们测试单个代码单元,而在 Web API 的集成测试中,我们测试所有协同工作的服务(内部和外部,包括第三方组件)。 应该进行服务调用以确保与外部服务的集成。

运行测试

让我们以我们在前一节中创建的单元测试应用为例:

  1. 添加一个用于集成测试的新项目,并确保项目结构如下截图所示:

  1. ProductTest.cs的构造函数中编写如下代码:
var server = new TestServer
(
  new WebHostBuilder()
  .UseStartup<TestStartup>()
);
_client = server.CreateClient();

在前面的代码块中,我们初始化了TestServer,其中我们使用TestStartup作为我们的启动条目文件。 最后,我们创建了WebHostBuilder()中的private readonly HttpClient _client;

  1. 然后,编写一个调用 productlist 资源的简单方法:
[Fact]
public async Task ReturnProductList()
{
  // Act
  var response = await _client.GetAsync("api/Product
  /productlist");
  response.EnsureSuccessStatusCode();
  var responseString = await response.Content.ReadAsStringAsync();
  // Assert
  Assert.NotEmpty(responseString);
}

在前面的代码中,我们正在使用我们的资源GET api/product/productlist并测试它,看它是否返回预期的输出。

为了平稳地运行代码,需要在代码中添加Microsoft.AspNetCore.Hosting;Microsoft.AspNetCore.TestHost;名称空间。

该测试还确保内部组件(或此方法进行的任何外部服务调用)按预期工作。

  1. 完成ProductTes.cs的代码如下:
namespace Chap06_02_Test.Services
{
  public class ProductTest
  {
    public ProductTest()
    {
      // Arrange
      var server = new TestServer(new WebHostBuilder()
      .UseStartup<TestStartup>());
      _client = server.CreateClient();
    }
    private readonly HttpClient _client;
    [Fact]
    public async Task ReturnProductList()
    {
      // Act
      var response = await
      _client.GetAsync("api/Product/productlist");
      response.EnsureSuccessStatusCode();
      var responseString = await
      response.Content.ReadAsStringAsync();
      // Assert
      Assert.NotEmpty(responseString);
    }
  }
}
  1. TestStartup文件编写如下代码:
namespace Chap06_02_Test
{
  public class TestStartup : Startup
  {
    public TestStartup(IConfiguration configuration) : 
    base(configuration)
    {  }
    public static IConfiguration InitConfiguration()
    {
      var config = new ConfigurationBuilder()
      .AddJsonFile("appsettings.json")
      .Build();
      return config;
    }
    public override void ConfigureServices(
    IServiceCollection services)
    {
      //mock context
      services.AddDbContext<ProductContext>
      (
        o => o.UseSqlServer
        (
          InitConfiguration().GetConnectionString
          (
            "ProductConnection"
          )
        )
      );
      services.AddMvc();
      services.AddScoped<IProductRepository, 
      ProductRepository>();
    }
    public override void Configure
    (
      IApplicationBuilder app, IHostingEnvironment env
    )
    {
      app.UseStaticFiles();
      app.UseMvc();
    }
  }
}

在前面的代码中,我们的TestStartup类继承了Startup类,这意味着我们现在使用它的成员和方法。

您需要将方法ConfigureServicesConfigure设置为虚拟,以便在TestStartup类中重写这些方法。

看看我们的InitConfiguration()方法; 此方法将添加您的测试配置文件,以便您可以在任何其他环境中使用测试配置值。

在我们的TestStartup类中,我们覆盖了ConfigureServicesConfigure方法,这样我们就可以配置测试服务或为测试目的而专门创建的任何实用程序类。

现在,我们已经设置好运行测试、打开测试资源管理器并运行选定的测试。 您还可以从ProductTest.cs文件运行测试(只需右键单击并选择 run tests)。

如果您需要调试代码,您也可以调试测试。 如果你这样做,你会得到以下结果:

您可以编写任意多的测试。 测试还取决于要测试的代码。

假的物品

顾名思义,假物品是指不真实的物品。 伪对象用于测试目的,包含实际的代码,但不具有真正的所有功能。 例如,我们可以使用 Entity Framework Core 创建一个假对象来获取数据记录; 在本例中,我们更喜欢使用 InMemory(https://docs.microsoft.com/en-us/ef/core/miscellaneous/testing/in-memory)而不是直接的 DB 连接。

运行测试

让我们以在前一节单元测试中开发的应用为例。 按照上一节中提到的步骤添加一个新的 xUnit 测试项目。

我们正在寻找用于测试目的的假对象或数据,因此我们不会攻击实际的数据库服务器。 相反,这里我们将使用 InMemory 数据库。

需要添加Microsoft.EntityFrameworkCore.InMemoryNuGet 包启动 InMemory 数据库。

我们不打算在这里改变任何东西,但我们将创建假数据和记录来测试。 要继续,请在ConfigureServices方法中向TestStartup.cs文件中添加以下代码:

//for tests use InMemory db
services.AddDbContext<ProductContext>
(
  o => o.UseInMemoryDatabase
  (
    InitConfiguration().GetConnectionString
    (
      "ProductConnection"
    )
  )
);

这里,我们使用以下方法:

  • 通过将.UseInMemoryDatabase添加到TestStartup类,只用于测试目的的 InMemory 数据库
  • 对于我们的实际代码,我们的数据库服务器将在Startup.cs类中保持不变,即.UseSqlServer

现在我们需要伪造数据和记录,所以在TestStartup类中添加如下方法:

private static void FakeData(DbContext context)
{
  var category = new Category
  {
    Id = ToGuid("A5DBF00D-2E29-4993-A0CA-7E861272C6DC"),
    Description = "Technical Videos",
    Name = "Videos"
  };
  context.Add(category);
  var product = new Product
  {
    Id = ToGuid("02341321-C20B-48B1-A2BE-47E67F548F0F"),
    CategoryId = category.Id,
    Description = "Microservices for .NET Core",
    Image = "microservices.jpeg",
    Name = "Microservices for .NET",
    Price = 651,
    InStock = 5
  };
  context.Add(product);
  context.SaveChanges();
}

然后从Configure(IApplicationBuilder app, IHostingEnvironment env)方法中调用FakeData(context)方法,如下代码所示:

public override void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    var context = app.ApplicationServices.GetService<ProductContext>();
    FakeData(context);
    app.UseStaticFiles();
    app.UseMvc();
}

现在我们已经准备好运行测试,因此打开测试资源管理器并点击 run All。 如果测试通过,您应该看到如下截图:

为了再次检查测试是否没有击中实际的数据库,让我们调试测试代码。 打开ProductTest.cs类并为以下测试设置断点:

[Fact]
public async Task ReturnProductList()
{
  // Act
  var response = await _client.GetAsync("api/Product/productlist");
  response.EnsureSuccessStatusCode();
  var responseString = await response.Content.ReadAsStringAsync();
  // Assert
  Assert.NotEmpty(responseString);
}

现在右键单击 Debug Test,使用 step-into(F11键)进入控制器和存储库,并检查产品的实际列表。 您可以看到我们的测试返回了假数据,这意味着它们没有击中实际的数据库。 下面是调试代码的屏幕截图:

前面的截图来自一个小应用,我们用它演示了使用假对象进行测试。 使用这种测试方法,我们的假对象总是会被击中,而不是任何实际的代码。

使用 Postman、Advanced REST Client 等测试服务调用

有很多工具可用来测试 RESTful web 服务和 api。 这些工具提供实际的输出。

当您只有 API 资源,并且希望在不同的场景中测试预期的输出,但没有实际的源代码时,Web 服务测试工具非常有用。

我们将使用以下两个工具测试我们的产品 api。

邮递员

Postman(https://www.getpostman.com/)是测试 web 服务输出时最流行的工具之一。 它还带有一个谷歌 Chrome 扩展:

  1. 启动邮差。 如果你没有它,从前面的链接安装它。
  2. 选择 Resource 类型为 GET 并输入 API 的 URL; 在我们的例子中,它是http://localhost:60431/api/Product/productlist
  3. 点击发送(或者,你可以点击发送和下载,如果你需要文件中的数据)。
  4. 如果测试通过,你会看到如下截图:

先进的 Rest 客户端

高级休息客户端(ARC)是另一个流行的工具,也作为一个 Chrome 扩展。 您可以安装它从 Chrome 扩展商店或直接从https://install.advancedrestclient.com/:

  1. 安装 Chrome 扩展为 ARC,如果还没有安装。
  2. 发射弧。
  3. 传递 GET 资源。
  4. 如果测试通过,你会看到如下截图:

用户验收测试

顾名思义,用户验收测试(UAT)是由用户完成或被用户接受的测试。 在这种测试方法中,可能是应用最终用户的用户直接参与测试。 可能存在用户在生产环境中测试的场景,或者用户可以访问他们可以接受或拒绝的预测试结果。

这种类型的测试取决于将在生产环境中使用应用的实际用户。 这种测试通常在 UAT 或预生产环境中进行。

行业中的典型环境被称为开发、阶段、QA、UAT、预生产和生产。 在您的组织中,您可能没有符合项目需求的所有环境; 如果是,请参考https://www.guru99.com/user-acceptance-testing.html

UAT 测试也被视为最终测试,它的接受或拒绝告诉我们当前的版本是否将被部署到生产环境中。 此测试的主要焦点是与业务相关的。 此测试不处理测试代码或各种模式的实现; 它只是确保实现了所有的业务规则和需求。

性能或负载测试

对于 web 应用的性能来说,可伸缩性是非常重要的。 一个应用可以是非常安全的、经过良好测试的、用好的代码创建的,但如果它不可伸缩,用户仍然会避免使用它。

我们将在第 9 章伸缩 RESTful web 服务(web 服务性能)中详细讨论 RESTful web 服务的伸缩。

性能对于一个好的 API 来说是非常重要的,所以我们需要测试并确保我们的应用能够加载或处理大的请求。 负载测试是一种非功能类型的测试(https://www.guru99.com/non-functional-testing.html),负载测试的主要目的不是验证代码或测试代码的运行状况。

这个测试的主要目的是确保 web API 在可扩展性、可靠性等方面表现良好。

以下是性能测试的技术或类型:

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

在 ASP.NET Core,我们可以使用以下方法来执行负载测试:

  • Visual Studio:如果你有 Visual Studio 企业版,你可以轻松地创建一个负载测试项目; 更多信息请访问以下链接:https://docs.microsoft.com/en-us/vsts/load-test
  • WebSurge:这是一个用于 api 的负载测试。 您可以在云中使用它,也可以免费用于学习目的。 更多信息,请访问http://websurge.west-wind.com/
  • BenchmarkDotNet:这个工具告诉我们有多少代码是性能的。 它测试不同的代码块,得到相同的结果,看看哪个性能最好。 更多信息,请访问https://github.com/dotnet/BenchmarkDotNet
  • Netling:这是一个用于 web 应用的负载测试工具。 使用 Netling,您可以更改并重新测试您的代码,以满足您的性能尺度。 更多信息,请访问https://github.com/hallatore/Netling

Explanations, along with working examples, of these tools and Visual Studio Load Testing is beyond the scope of this book.

在本节中,我们将简单地测试我们的产品 api,以检查它们列出我们所请求的产品所花费的时间。

您还可以使用一个简单的 web 客户端测试 api 的请求时间。 在第 10 章构建 Web 客户端(消费 Web 服务)中,我们将详细讨论如何构建 Web 客户端。

看看我们的ProductTest类的代码,如下:

public class ProductTest
  {
    public ProductTest(ITestOutputHelper output)
    {
      _output = output;
    }
    private const double ExpectedRequestTime = 1000;
    private const int ApiLoad = 100;
    private const string RequestUri = 
    "http://localhost:60431/api/product/productlist";
    private readonly ITestOutputHelper _output;
    private static double RequestCallTime()
    {
      DateTime start;
      DateTime end;
      using (var client = new HttpClient())
      {
        start = DateTime.Now;
        var response = client.GetAsync(RequestUri).Result;
        end = DateTime.Now
      }
      var actual = (end - start).TotalMilliseconds;
      return actual;
    }
    [Fact]
    public void SingleCallRequestTime()
    {
      var actual = RequestCallTime();
      _output.WriteLine($"Actual time: {ExpectedRequestTime}
      millisecond. 
      Expected time: {actual} millisecond.");
      Assert.True(actual <= ExpectedRequestTime);
    }
    //code truncated
}

前面的代码是不言自明的。 我们只是计算单个和多个请求所花费的时间,并检查它是否达到我们的基准。

The complete code is available to download from https://github.com/PacktPublishing/Building-RESTful-Web-Services-with-DotNET-Core.

运行测试

要运行测试,您需要确保您的 api 正在运行,并使用 URL 进行访问。 请使用 CLI 完成以下步骤:

  1. 打开 Visual Studio 命令提示符
  2. 找到 API 项目的文件夹
  3. 启动命令dotnet run

你现在应该看到一个类似于下面截图的屏幕:

按照以下步骤使用 Visual Studio 测试资源管理器运行测试:

  1. 打开ProductTest.cs文件
  2. 开启测试资源管理器
  3. 单击“运行”

这将运行所有测试; 你应该看到类似下面截图的输出:

我们还可以检查各个 api 完成请求所花费的确切时间。 要做到这一点,单击特定TestCase结果的测试资源管理器中的Output,您应该会看到以下屏幕:

还可以使用命令行运行这些测试,如下所示:

  1. 打开 Visual Studio 命令提示符
  2. 找到 API 项目的文件夹
  3. 启动命令dotnet test

前面的命令将运行所有测试; 如果它们通过,你应该看到以下屏幕:

Visit https://docs.microsoft.com/en-us/dotnet/core/tools/?tabs=netcore2x to check all the available CLI commands.

在本节中,我们尝试了一个基于请求时间的简单负载测试。 我们试过一个电话和多个电话。

总结

测试有助于确保我们的代码没有错误。 测试也是所有想要使他们的代码干净和可维护的开发人员的实践。 在本章中,我们介绍了开发团队日常活动中的测试范例,了解了存根和模拟,以及理解集成、安全性和性能测试的重要性。

在接下来的章节中,我们将讨论安全性,包括遵循 OWASP 安全标准和 JWT 身份验证。 我们将介绍使用自定义过滤器和输入验证的更复杂的场景。 对于任何 web 应用来说,数据保护总是一个高度优先级的问题,因此我们还将关注敏感数据的持久性和存储。*