[ABP教程]第四章 集成测试
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框架.
 
本教程分为以下部分:
- Part 1: 创建服务端
 - Part 2: 图书列表页面
 - Part 3: 创建,更新和删除图书
 - Part 4: 集成测试(本章)
 - Part 5: 授权
 - Part 6: 作者: 领域层
 - Part 7: 作者: 数据库集成
 - Part 8: 作者: 应用服务层
 - Part 9: 作者: 用户页面
 - Part 10: 图书到作者的关系
 
下载源码
本教程根据你的UI 和 Database偏好有多个版,我们准备了两种可供下载的源码组合:
解决方案中的测试项目
这一部分涵盖了 服务器端 测试. 解决方案中有多个测试项目:

每个项目用于测试相关的应用程序项目.测试项目使用以下库进行测试:
- xunit 作为主测试框架.
 - Shoudly 作为断言库.
 - NSubstitute 作为模拟库.
 
{{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教程]第四章 集成测试的更多相关文章
- [Learn Android Studio 汉化教程]第四章 : Refactoring Code
		
[Learn Android Studio 汉化教程]第四章 : Refactoring Code 第四章 Refactoring Code 重构代码 在Android Studio中开发,解决 ...
 - [ABP教程]第七章 作者:数据库集成
		
Web开发教程7 作者:数据库集成 关于此教程 在这个教程系列中,你将要构建一个基于ABP框架的应用程序 Acme.BookStore.这个应用程序被用于甘丽图书页面机器作者.它将用以下开发技术: E ...
 - [ABP教程]第六章 作者:领域层
		
Web开发教程6 作者:领域层 关于此教程 在这个教程系列中,你将要构建一个基于ABP框架的应用程序 Acme.BookStore.这个应用程序被用于甘丽图书页面机器作者.它将用以下开发技术: Ent ...
 - [ABP教程]第五章 授权
		
原文档 地址: Web Application Development Tutorial - Part 5: Authorization 关于此教程 在这个教程系列中,您将构建一个基于ABP的Web应 ...
 - [ABP教程]第三章 创建、更新和删除图书
		
Web应用程序开发教程 - 第三章: 创建,更新和删除图书 关于本教程 在本系列教程中, 你将构建一个名为 Acme.BookStore 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以 ...
 - 2018-06-20 中文代码示例视频演示Python入门教程第四章 控制流
		
知乎原链 续前作: 中文代码示例视频演示Python入门教程第三章 简介Python 对应在线文档: 4. More Control Flow Tools 录制中出了不少岔子. 另外, 输入法确实是一 ...
 - ABP教程(四)- 开始一个简单的任务管理系统 - 实现UI端的增删改查
		
接上一篇:ABP教程(三)- 开始一个简单的任务管理系统 – 后端编码 1.实现UI端的增删改查 1.1添加增删改查代码 打开SimpleTaskSystem.sln解决方案,添加一个“包含视图的MV ...
 - python 教程 第四章、 控制流
		
第四章. 控制流 控制语句后面要加冒号: 1) if语句 if guess == number: print 'Congratulations, you guessed it.' # New b ...
 - Cobalt Strike系列教程第四章:文件/进程管理与键盘记录
		
Cobalt Strike系列教程分享如约而至,新关注的小伙伴可以先回顾一下前面的内容: Cobalt Strike系列教程第一章:简介与安装 Cobalt Strike系列教程第二章:Beacon详 ...
 
随机推荐
- Thinkphp V5.X 远程代码执行漏洞 - POC(搬运)
			
文章来源:lsh4ck's Blog 原文链接: https://www.77169.com/html/237165.html Thinkphp 5.0.22 http://192.168.1.1 ...
 - C# 高性能对象映射
			
1.之前在使用AutoMapper 框架感觉用着比较不够灵活,而且主要通过表达式树Api 实现对象映射 ,写着比较讨厌,当出现复杂类型和嵌套类型时性能直线下降,甚至不如序列化快. 2.针对AutoMa ...
 - 教你30秒解开手机的密码 适用于高通CPU
			
教程简介 先将手机进入9008模式.进入方法请自己百度. 进入9008方法如下: 先将手机关机,然后按住音量加和音量减不松手. 使用教程: 将数据线拆入电脑.会出现一个端口 出现端口后可以松开 ...
 - sql注入之双查询注入
			
双查询注入前需要了解什么是子查询 子查询可以理解在一个select语句中再插入一个select 里面的select语句就是子查询 例子:select concat((select database() ...
 - 在github上删除项目或某个文件(两种方式)
			
一.使用命令删除 首先先上传一个文件到远程仓库,测试一下 输入命令git rm -r --cached 文件名 删除本地跟暂存区的文件,如下图 再输入命令 git commit -m "删除 ...
 - PHP代码审计学习-PHP-Audit-Labs-day2
			
filter_var()函数 filter_var() 函数通过指定的过滤器过滤一个变量.如果成功,则返回被过滤的数据.如果失败,则返回 FALSE. filter_var(variable, fil ...
 - 牛客挑战赛46 D
			
题目链接: 数列 查询有多少\([l,r]\)区间满足每个数出现\(k\)的倍数次 即为\(1\)到\(r\)与\(1\)到\(l-1\)每个数相减的次数为\(k\)的倍数次 可以使用哈希维护 记录每 ...
 - 面试 11-02.ES6
			
11-02.ES6 #Class和普通构造函数有何区别 我们经常会用ES6中的Class来代替JS中的构造函数做开发. Class 在语法上更加贴合面向对象的写法 Class 实现继承更加易读.易理解 ...
 - [日常摸鱼]bzoj3122 [Sdoi]2013 随机数生成器
			
又是写了一晚上才过的题- 题意:有一个数列$x_n=(ax_{n-1}+b) mod p$,给你$x_1,a,b,p,t$,求最小的$x_i=t$的$i$,可能不存在 一开始很自然的推出了式子$x_n ...
 - 网站开发学习Python实现-Django学习-自学注意(6.1.3)
			
@ 目录 1.配置文件相关 2.应用创建相关 3.项目相关 4.模板相关 5.其他 关于作者 1.配置文件相关 1.可以更改时间,地区相关(国际化) 2.BASE_DIR很重要,一个工程要有很好的移植 ...