好代码是管出来的——.Net Core集成测试与数据驱动测试
软件的单元测试关注是的软件最小可执行单元是否能够正常执行,但是软件是由一个个最小执行单元组成的集合体,单元与单元之间存在着种种依赖或联系,所以在软件开发时仅仅确保最小单元的正确往往是不够的,为了保证软件能够正确运行,单元与单元之间的集成测试是非常必要。
另外上一篇文章只是介绍了如何使用xUnit.net对.Net Core程序进行简单(无参)的单元测试以及计算代码的覆盖率,但是在实际的测试工作中,往往会通过语句覆盖、条件/分支覆盖(白盒)方式以及等价类、边界值等(黑盒)方式来设计测试用例,这些用例的测试的关键点在于传入测试方法的参数是不同的,如果使用无参测试方法那么针对不同的测试用例就需要编写大量的测试方法,这是不现实的。
本文将从以下几个方面,在单元测试的基础上介绍如何完成数据驱动测试以及代码依赖的集成测试:
集成测试简介
集成测试简单的理解就是在单元测试的基础上,将各个单元根据其依赖关系集成起来,检查代码是否能够正确运行,集成测试根据软件开发方式的不同或者软件架构不同也有不同的集成方式,比如面向过程的设计方式和面向对象设计方式、单体架构以及微服务分布式架构等等。
本文涉及到的仅仅是面向对象编程中类与类之间依赖的集成测试。
使用xUnit.Net实现简单的数据库集成测试
数据库是大部分软件系统不可缺少的组件,所以在集成测试时与数据库集成是一种常见的测试场景,下面就开始介绍如何通过xUnit.Net来测试。
1. 编写被测试代码:
在被测试项目中引入EFCore以及相关组件:
添加DBContext以及相应的数据操作代码:
注:此处使用LocalDB作为数据库完成测试,如果使用SQL Server修改连接字符串即可。
用户仓储中实现了用户添加以及根据姓名查询的方法。
注:完成代码编写后,需要将实体代码迁移到数据库,先通过Add-Migration添加迁移代码,然后通过update-database命令将数据更新到数据库,迁移方法可参考:https://docs.microsoft.com/en-us/ef/core/get-started/full-dotnet/new-db
2.编写测试代码:
测试代码也非常简单,就是创建一个SQLServer的用户仓储实例,然后通过构造的方式将其注入到UserManager类型中使用,后续UserManager的创建、查询用户的方法将通过该仓储实现。
3.测试运行结果:
可回归的数据库集成测试
这里的回归指的是回归测试,回归测试是指当修改了旧的代码后需要重新对旧代码进行测试确认此次修改没有影响到已有代码。一般来说使用xUnit.Net实现的测试均是可回归的,因为测试代码不变,且测试代码可以重复执行,另外测试代码也被代码版本管理工具管理,当修改代码后重新执行所有测试方法即可。
但是与数据库相关的测试又有所区别,数据库是数据持久化工具,对数据的每一次操作都会被持久化,假设有两个测试用例,第一是添加一条数据,第二是查询这条数据的数量是否为1,那么这两个测试在第一次运行的时候是没有问题的,但是再次运行时第一个用例可能主键冲突无法被添加,或者添加成功后第二个用例查询结果为2,导致测试失败,这样的测试视为不可回归。
下面就介绍如何使用xUnit.Net实现数据库集成的回归测试:
使用构造方法初始化数据库
要实现数据库集成测试的可回归性,其实最主要的问题在于每次执行完单元测试后数据库的状态都是不同的,那么解决这个问题在每次执行测试前将数据库初始化是否就可以解决了呢?
看下面代码:
首先为测试类添加构造方法,在构造方法中初始化数据库。
然后在测试方法中添加用户数量测试断言,当添加完成用户后,应该一共存在1个用户。
执行测试方法:
无论执行多少次测试都是通过的,这意味着每次添加用户后用户数量均为1,这样就达到了测试的可回归性。
但是真实的开发过程中,不可能只有一个测试类型,而且如果为每一个测试类型都添加初始化数据库的代码,那么这些单元测试将会执行的非常慢(数据库的初始化非常耗时),那么要如何解决这个问题?
使用Fixture共享测试上下文
Fixture是xUnit.Net中的一项特性,它可以用来共享测试上下文,Fixture可以译为固定装置,换句话说就是把一个内容固定住,使该内容可以在被指定的范围内使用。xUnit.Net中Fixture有两种类型,分别是ClassFixture和CollectionFixture,它们对应的是在一个测试类型里面共享和一个测试类型集合中共享上下文,Fixture的作用有很多,下面将介绍如何使用Fixture来共享数据库被初始化这一状态(不需要重复初始化):
1. 创建一个Fixture类型:
Fixture实际上是一个普通的C#类型,唯一需要注意的是它使用构造方法初始化数据、使用Dispose来释放或者清理数据,简单的一个Fixture类型如下:
2. 为单个类型使用ClassFixture:
从代码中可以看到通过构造方法初始化的代码已经被注释了,实现IClassFixture接口后,再次执行测试方法,可以看到测试仍然是可回归的。
3. 为多个类型使用ICollectionFixture:
首先需要在DatabaseFixture的基础上添加一个实现ICollectionFixture的类型:
两个要点分别是需要使用CollectionDefinition特性,然后类型需要实现ICollectionFixture<T>接口。
在测试类型上通过Collection特性,指定之前定义的Collection名称来应用该CollectionFixture:
然后多次执行测试方法,可以发现测试仍然是可回归的:
注:在共享测试上下数据时(包括共享数据库数据),可能会因为测试方法执行顺序导致测试失败,但是xUnit.Net中没有提供按顺序执行实现方式,默认是按照方法命名,如果需要按照一定顺序执行测试,可参考官方的例子:https://github.com/xunit/samples.xunit/tree/master/TestOrderExamples
测试数据的管理与初始化
涉及到数据库的测试,很可能在测试之前数据库就会存在一些已有的测试数据,使用xUnit.Net来编写测试代码时,可以将测试数据的初始化放到Fixture类型中,但是要如何对这些数据进行管理呢?一般来说使用SQL脚本来管理数据库的测试数据是一种不错的方式,因为这样测试数据与代码解耦,维护数据时仅需要维护SQL脚本文件即可。
下面就介绍一下如何在xUnit.Net中使用SQL脚本文件管理以及初始化数据:
1.创建一个SQL脚本:
注:需要将文件的“复制到输出目录”设置为“总是复制”或者“有更新复制”:
2.在Fixture类型初始化数据库时,执行SQL脚本初始化数据:
下面代码首先去Data目录下查找.sql的所有文件,然后依次执行这些SQL脚本。
3.修改测试代码(因为脚本添加了2条数据,所以期望值应该是3条):
4.执行测试:
测试成功。
数据驱动测试简介
文章最开始提到过,不管是白盒的覆盖测试还是黑盒的边界/等价测试,它们最直接的体现就是“参数”,传入被测试方法的参数。而这些参数实际上就是“测试用例”,为每一个测试用例编写一个测试方法是不合理的,所以需要一种方法通过变换参数来完成测试,并且尽可能的重用测试代码。这就是数据驱动测试。
基于xUnit.Net的数据驱动测试
Theory&InlineData
Theory和InlineData是xUnit.Net中提供的数据驱动(参数化)测试的功能特性,下面是一个theory以及InlineData的应用示例:
运行结果:
通过InlineData就可以轻易的将测试用例的数据应用到测试方法上,并完成期望的测试。但是当添加新的测试用例时就需要对代码文件进行修改,测试用例放置在代码中管理也不是非常方便,有没有一种可以独立管理测试用例数据的方法呢?
基于excel的数据驱动测试
xUnit.Net没有提供内置的Excel数据加载功能,但是它的拓展性很好,可以通过实现DataAttribute实现一个Theory的数据源提供器:
1. 实现DataAttribute抽象类,实现Excel的Theory数据提供器:
下面是实现Excel数据加载的核心方法:
以上方法需要注意以下几点:
- 两个主体:Excel文件名称和测试的方法的MethodInfo,前者用于读取Excel文件,后者用来获取参数信息。
- 通过MethodInfo获取测试方法的参数列表包括参数类型。
- 读取Excel数据并于测试方法的参数列表匹配,简单来说就是Excel中每一行的数据就是对应测试方法的参数,有多少行数据测试方法就会被执行多少次。
- 当Excel中数据读取为double或者float但是测试方法需要int时,需要进行转换。
注:此处使用的Excel文件读取组件为:https://github.com/ExcelDataReader/ExcelDataReader
DataAttribute完整代码见:https://github.com/yqszt/xUnitTestDemo/blob/master/XUnitTestProject/ExcelDataAttribute.cs
2.使用ExcelDataAttribute:
3. 在AdditionTestCase.xls中添加测试用例数据:
注:需要将AdditionTestCase.xls的复制到输出目录属性设置为“总是复制”或“复制新文件”。
4.执行测试方法:
测试成功。
小结
本文主要介绍了.Net Core使用xUnit.Net实现了与数据的集成测试并通过Fixture的方式实现了数据库的初始化,保证了测试的可回归性。另外还介绍了如何使用xUnit.Net来实现数据驱动测试,数据驱动测试可以简单地通过添加用例数据的方式来提高测试质量,还可以便捷的对测试用例进行管理。
本文示例代码:https://github.com/yqszt/xUnitTestDemo
参考:
https://en.wikipedia.org/wiki/Integration_testing
https://en.wikipedia.org/wiki/Data-driven_testing
https://xunit.github.io/docs/getting-started-dotnet-core#write-first-theory
http://dotnetliberty.com/index.php/2015/12/31/fast-asp-net-5-integration-testing-with-xunit/
https://github.com/MisterJames/GenFu
http://ikeptwalking.com/writing-data-driven-tests-using-xunit/
https://wandering.life/data-driven-testing-xunit-vs2017/
https://github.com/ExcelDataReader/ExcelDataReader
http://xunitpatterns.com/Testing%20With%20Databases.html
https://xunit.github.io/docs/shared-context
本文链接:https://www.cnblogs.com/selimsong/p/9306221.html
好代码是管出来的——浅谈.Net Core的代码管理方法与落地(更新中...)
好代码是管出来的——.Net Core集成测试与数据驱动测试的更多相关文章
- 好代码是管出来的——.Net Core中的单元测试与代码覆盖率
测试对于软件来说,是保证其质量的一个重要过程,而测试又分为很多种,单元测试.集成测试.系统测试.压力测试等等,不同的测试的测试粒度和测试目标也不同,如单元测试关注每一行代码,集成测试关注的是多个模块是 ...
- 少量代码设计一个登录界面 - .NET CORE(C#) WPF开发
微信公众号:Dotnet9,网站:Dotnet9,问题或建议:请网站留言, 如果对您有所帮助:欢迎赞赏. 少量代码设计一个登录界面 - .NET CORE(C#) WPF开发 阅读导航 本文背景 代码 ...
- .NET Core系列 :4 测试
2016.6.27 微软已经正式发布了.NET Core 1.0 RTM,但是工具链还是预览版,同样的大量的开源测试库也都是至少发布了Alpha测试版支持.NET Core, 这篇文章 The Sta ...
- ASP.NET Core 集成测试
集成测试 集成测试,也叫组装测试或联合测试.在单元测试的基础上,将所有模块按照设计要求(如根据结构图)组装成为子系统或系统,进行集成测试. 实践表明,一些模块虽然能够单独地工作,但并不能保证连接起来也 ...
- ASP.NET Core 集成测试中通过 Serilog 向控制台输出日志
日志是程序员的雷达,不仅在生产环境中需要,在集成测试环境中也需要,可以在持续集成失败后帮助定位问题.与生产环境不同,在集成测试环境中使用控制台输出日志更方便,这样可以通过持续集成 runner 执行 ...
- dotnet core TargetFramework 解析顺序测试
dotnet core TargetFramework 解析顺序测试 Intro 现在 dotnet 的 TargetFramework 越来越多,抛开 .NET Framework 不谈,如果一个类 ...
- 2.10. 代码片段:demo方法(Core Data 应用程序实践指南)
该代码段我觉得没有太多东西 - (void)applicationDidBecomeActive:(UIApplication *)application { [self cdh]; [self de ...
- Ubuntu-Docker[1]安装Docker,通过Docker部署net core代码,需要结合[.NET Core 18]发布、ASP.NET Core Docker部署
1)通过系统自带包安装 通过自带包安装,可能Docker版本较旧 $ sudo apt-get update Reading package lists... Done $ sudo apt-get ...
- ASP.NET Core 集成测试中结合 WebApplicationFactory 使用 SQLite 内存数据库
SQLite 内存数据库(in-memory database)的连接字符串是 Data Source=:memory: ,它的特点是数据库连接一关闭,数据库就会被删除.而使用 services. ...
随机推荐
- 玩转Spring Cloud之服务注册发现(eureka)及负载均衡消费(ribbon、feign)
如果说用Spring Boot+Spring MVC是开发单体应用(或单体服务)的利器,那么Spring Boot+Spring MVC+Spring Cloud将是开发分布式应用(快速构建微服务)的 ...
- C# 设置Excel中的数字字符串格式
在Excel中,数字字符串用不同格式表示,可代表不同数据意义.例如在财务报表里需要用特定的数字字符串格式来反映金额信息.货币币种.数据精确程度.增减趋势等等.下面分享如何通过C#编程来设置Excel表 ...
- ArcGIS API for JavaScript 入门教程[6] 再讲数据——Map类之可操作图层
[回顾]上篇交代了Map是各种图层(不管是实际上的图层还是由图层构成的对象)的容器,是数据的容器,并不作显示(由视图类绘制).并重点讲解了由图层构成的复杂图层——高程属性ground和底图属性base ...
- MySQL数据库优化方案
优化索引.SQL语句.分析慢查询: 设计数据表的时候,严格根据数据库的设计范式来设计数据库表: 使用缓存,把经常访问的又不经常更改的数据放到缓存中,能减少磁盘I/O: 优化硬盘,使用SSD,使用磁盘队 ...
- Luogu P5309 [Ynoi2012]D1T1
小清新分块题,从头到尾都十分套路,和CXR大佬一起YY了一下就出来了 首先套路地设个阈值\(S\),把修改的\(x\)分成大于\(S\)和小于等于\(S\)两部分,然后我们考虑分别求解这两部分 \(x ...
- 5个常常被大家忽略的Python小技巧
下面我挑选出的这几个技巧常常会被人们忽略,但它们在日常编程中能真正的给我们带来不少帮助. 1. 字典推导(Dictionary comprehensions)和集合推导(Set comprehensi ...
- python maximum recursion depth exceeded 处理办法
1.在执行命令 pyinstaller -F D:\py\programe\banksystem.py打包生成.exe文件时报错:python maximum recursion depth exce ...
- Java相关面试题总结+答案(一)
[Java基础] 1. JDK 和 JRE 有什么区别? JDK:Java Development Kit 的简称,即Java开发工具包,提供了Java 的开发环境和运行环境. JRE:Java Ru ...
- mysql id从n 开始
mysql 全部删除数据后设置 id从1开始: truncate table table_name mysql 删除部分数据后设置 id从n开始 ALTER TABLE user auto_incr ...
- javaScript设计模式之面向对象编程(object-oriented programming,OOP)--寄生组合式继承
组合式继承:将类式继承同构造函数继承组合使用,但是存在一个问题,子类不是父类的实例,而子类的原型式父类的实例,所以才有了寄生组合式继承. 意思就是说,寄生就是寄生式继承,寄生式继承就是依托于原型继承, ...