Web应用程序开发教程 - 第三章: 集成测试

//[doc-params]
{
"UI": ["MVC","NG"],
"DB": ["EF","Mongo"]
}

{{

if UI == "MVC"

UI_Text="mvc"

else if UI == "NG"

UI_Text="angular"

else

UI_Text="?"

end

if DB == "EF"

DB_Text="Entity Framework Core"

else if DB == "Mongo"

DB_Text="MongoDB"

else

DB_Text="?"

end

}}

关于本教程

在本系列教程中, 你将构建一个名为 Acme.BookStore 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以下技术开发的:

  • {{DB_Text}} 做为ORM提供程序.
  • {{UI_Value}} 做为UI框架.

本教程分为以下部分:

下载源码

本教程根据你的UIDatabase偏好有多个版,我们准备了两种可供下载的源码组合:

解决方案中的测试项目

这一部分涵盖了 服务器端 测试. 解决方案中有多个测试项目:

每个项目用于测试相关的应用程序项目.测试项目使用以下库进行测试:

{{if DB=="EF"}}

测试项目配置为使用 SQLite内存 作为数据库. 创建一个单独的数据库实例并使用数据种子系统进行初始化种子数据,为每个测试准备一个新的数据库.

{{else if DB=="Mongo"}}

Mongo2Go库用于模拟MongoDB数据库. 创建一个单独的数据库实例并使用数据种子系统进行初始化种子数据,为每个测试准备一个新的数据库.

{{end}}

添加测试数据

如果你已经按照第一部分中的描述创建了数据种子贡献者,则相同的数据也在测试中可用. 因此你可以跳过此部分. 如果你尚未创建种子贡献者,可以使用 BookStoreTestDataSeedContributor 来为要在以下测试中使用的相同数据提供种子.

测试 BookAppService

Acme.BookStore.Application.Tests 项目中创建一个名叫 BookAppService_Tests 的测试类:

using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Application.Dtos;
using Xunit; namespace Acme.BookStore.Books
{ {{if DB=="Mongo"}}
[Collection(BookStoreTestConsts.CollectionDefinitionName)]{{end}}
public class BookAppService_Tests : BookStoreApplicationTestBase
{
private readonly IBookAppService _bookAppService; public BookAppService_Tests()
{
_bookAppService = GetRequiredService<IBookAppService>();
} [Fact]
public async Task Should_Get_List_Of_Books()
{
//Act
var result = await _bookAppService.GetListAsync(
new PagedAndSortedResultRequestDto()
); //Assert
result.TotalCount.ShouldBeGreaterThan(0);
result.Items.ShouldContain(b => b.Name == "1984");
}
}
}
  • 测试方法 Should_Get_List_Of_Books 直接使用 BookAppService.GetListAsync 方法来获取用户列表,并执行检查.
  • 我们可以安全地检查 "1984" 这本书的名称,因为我们知道这本书可以在数据库中找到,我们已将其添加到种子数据中.

新增测试方法,用以测试创建一个合法book实体的场景:

[Fact]
public async Task Should_Create_A_Valid_Book()
{
//Act
var result = await _bookAppService.CreateAsync(
new CreateUpdateBookDto
{
Name = "New test book 42",
Price = 10,
PublishDate = System.DateTime.Now,
Type = BookType.ScienceFiction
}
); //Assert
result.Id.ShouldNotBe(Guid.Empty);
result.Name.ShouldBe("New test book 42");
}

新增测试方法,用以测试创建一个非法book实体失败的场景:

[Fact]
public async Task Should_Not_Create_A_Book_Without_Name()
{
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () =>
{
await _bookAppService.CreateAsync(
new CreateUpdateBookDto
{
Name = "",
Price = 10,
PublishDate = DateTime.Now,
Type = BookType.ScienceFiction
}
);
}); exception.ValidationErrors
.ShouldContain(err => err.MemberNames.Any(mem => mem == "Name"));
}
  • 由于 Name 是空值, ABP 抛出一个 AbpValidationException 异常.

最终的测试类如下所示:

using System;
using System.Linq;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Validation;
using Xunit; namespace Acme.BookStore.Books
{ {{if DB=="Mongo"}}
[Collection(BookStoreTestConsts.CollectionDefinitionName)]{{end}}
public class BookAppService_Tests : BookStoreApplicationTestBase
{
private readonly IBookAppService _bookAppService; public BookAppService_Tests()
{
_bookAppService = GetRequiredService<IBookAppService>();
} [Fact]
public async Task Should_Get_List_Of_Books()
{
//Act
var result = await _bookAppService.GetListAsync(
new PagedAndSortedResultRequestDto()
); //Assert
result.TotalCount.ShouldBeGreaterThan(0);
result.Items.ShouldContain(b => b.Name == "1984");
} [Fact]
public async Task Should_Create_A_Valid_Book()
{
//Act
var result = await _bookAppService.CreateAsync(
new CreateUpdateBookDto
{
Name = "New test book 42",
Price = 10,
PublishDate = System.DateTime.Now,
Type = BookType.ScienceFiction
}
); //Assert
result.Id.ShouldNotBe(Guid.Empty);
result.Name.ShouldBe("New test book 42");
} [Fact]
public async Task Should_Not_Create_A_Book_Without_Name()
{
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () =>
{
await _bookAppService.CreateAsync(
new CreateUpdateBookDto
{
Name = "",
Price = 10,
PublishDate = DateTime.Now,
Type = BookType.ScienceFiction
}
);
}); exception.ValidationErrors
.ShouldContain(err => err.MemberNames.Any(mem => mem == "Name"));
}
}
}

打开测试资源管理器(测试 -> Windows -> 测试资源管理器)并执行所有测试:

恭喜你, 绿色图标表示测试已成功通过!

下一章

查看本教程的下一章.

[ABP教程]第四章 集成测试的更多相关文章

  1. [Learn Android Studio 汉化教程]第四章 : Refactoring Code

    [Learn Android Studio 汉化教程]第四章 : Refactoring Code 第四章 Refactoring Code    重构代码 在Android Studio中开发,解决 ...

  2. [ABP教程]第七章 作者:数据库集成

    Web开发教程7 作者:数据库集成 关于此教程 在这个教程系列中,你将要构建一个基于ABP框架的应用程序 Acme.BookStore.这个应用程序被用于甘丽图书页面机器作者.它将用以下开发技术: E ...

  3. [ABP教程]第六章 作者:领域层

    Web开发教程6 作者:领域层 关于此教程 在这个教程系列中,你将要构建一个基于ABP框架的应用程序 Acme.BookStore.这个应用程序被用于甘丽图书页面机器作者.它将用以下开发技术: Ent ...

  4. [ABP教程]第五章 授权

    原文档 地址: Web Application Development Tutorial - Part 5: Authorization 关于此教程 在这个教程系列中,您将构建一个基于ABP的Web应 ...

  5. [ABP教程]第三章 创建、更新和删除图书

    Web应用程序开发教程 - 第三章: 创建,更新和删除图书 关于本教程 在本系列教程中, 你将构建一个名为 Acme.BookStore 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以 ...

  6. 2018-06-20 中文代码示例视频演示Python入门教程第四章 控制流

    知乎原链 续前作: 中文代码示例视频演示Python入门教程第三章 简介Python 对应在线文档: 4. More Control Flow Tools 录制中出了不少岔子. 另外, 输入法确实是一 ...

  7. ABP教程(四)- 开始一个简单的任务管理系统 - 实现UI端的增删改查

    接上一篇:ABP教程(三)- 开始一个简单的任务管理系统 – 后端编码 1.实现UI端的增删改查 1.1添加增删改查代码 打开SimpleTaskSystem.sln解决方案,添加一个“包含视图的MV ...

  8. python 教程 第四章、 控制流

    第四章. 控制流 控制语句后面要加冒号: 1)    if语句 if guess == number: print 'Congratulations, you guessed it.' # New b ...

  9. Cobalt Strike系列教程第四章:文件/进程管理与键盘记录

    Cobalt Strike系列教程分享如约而至,新关注的小伙伴可以先回顾一下前面的内容: Cobalt Strike系列教程第一章:简介与安装 Cobalt Strike系列教程第二章:Beacon详 ...

随机推荐

  1. PyQt(Python+Qt)学习随笔:Qt Designer中窗口对象的windowFilePath属性

    windowFilePath属性仅对窗口对象有效,用于关联一个窗口和对应的文件及路径. 当窗口没有设置标题属性的情况下,则窗口标题展示展示windowFilePath对应的文件名的信息(路径信息不展示 ...

  2. 手机APP测试(测试点、测试流程、功能测试)

    1.功能测试 1.1 启动 APP安装完成后,是否可以正常打开,稳定运行 APP的速度是可以让人接受,切换是否流畅 网络异常时,应用是否会崩溃:在请求超时的情况下,如果程序逻辑处理的不好,就有可能发生 ...

  3. ActionResult的返回类型

    ActionResult是控制器方法执行后返回的结果类型,控制器方法可以返回一个直接或间接从ActionResult抽象类继承的类型,如果返回的是非ActionResult类型,控制器将会将结果转换为 ...

  4. 题解-The Number of Good Intervals

    题面 The Number of Good Intervals 给定 \(n\) 和 \(a_i(1\le i\le n)\),\(m\) 和 \(b_j(1\le j\le m)\),求对于每个 \ ...

  5. Oracle 迁移数据库到 mysql

    一. oracle导出.sql文件(Navicat Premiu  11.0.8  无法实现oracle到mysql的数据传输亲测有效) exp username/pass@数据局地址ip:1521/ ...

  6. 群晖DS218+部署Harbor(1.10.3)

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  7. 【Python】自动化测试的7个步骤

    我们对自动化测试充满了希望,然而,自动化测试却经常带给我们沮丧和失望.虽然,自动化测试可以把我们从困难的环境中解放出来,在实施自动化测试解决问题的同时,又带来同样多的问题.在开展自动化测试的工作中,关 ...

  8. pandas的学习2-选择数据

    import pandas as pd import numpy as np dates = pd.date_range('20130101', periods=6) df = pd.DataFram ...

  9. 一文搞懂 CountDownLatch 用法和源码!

    CountDownLatch 是多线程控制的一种工具,它被称为 门阀. 计数器或者 闭锁.这个工具经常用来用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用).下面我们就来一起 ...

  10. Python 写了一个批量生成文件夹和批量重命名的工具

    Python 写了一个批量生成文件夹和批量重命名的工具 目录 Python 写了一个批量生成文件夹和批量重命名的工具 演示 功能 1. 可以读取excel内容,使用excel单元格内容进行新建文件夹, ...