软件的单元测试关注是的软件最小可执行单元是否能够正常执行,但是软件是由一个个最小执行单元组成的集合体,单元与单元之间存在着种种依赖或联系,所以在软件开发时仅仅确保最小单元的正确往往是不够的,为了保证软件能够正确运行,单元与单元之间的集成测试是非常必要。
  另外上一篇文章只是介绍了如何使用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集成测试与数据驱动测试的更多相关文章

  1. 好代码是管出来的——.Net Core中的单元测试与代码覆盖率

    测试对于软件来说,是保证其质量的一个重要过程,而测试又分为很多种,单元测试.集成测试.系统测试.压力测试等等,不同的测试的测试粒度和测试目标也不同,如单元测试关注每一行代码,集成测试关注的是多个模块是 ...

  2. 少量代码设计一个登录界面 - .NET CORE(C#) WPF开发

    微信公众号:Dotnet9,网站:Dotnet9,问题或建议:请网站留言, 如果对您有所帮助:欢迎赞赏. 少量代码设计一个登录界面 - .NET CORE(C#) WPF开发 阅读导航 本文背景 代码 ...

  3. .NET Core系列 :4 测试

    2016.6.27 微软已经正式发布了.NET Core 1.0 RTM,但是工具链还是预览版,同样的大量的开源测试库也都是至少发布了Alpha测试版支持.NET Core, 这篇文章 The Sta ...

  4. ASP.NET Core 集成测试

    集成测试 集成测试,也叫组装测试或联合测试.在单元测试的基础上,将所有模块按照设计要求(如根据结构图)组装成为子系统或系统,进行集成测试. 实践表明,一些模块虽然能够单独地工作,但并不能保证连接起来也 ...

  5. ASP.NET Core 集成测试中通过 Serilog 向控制台输出日志

    日志是程序员的雷达,不仅在生产环境中需要,在集成测试环境中也需要,可以在持续集成失败后帮助定位问题.与生产环境不同,在集成测试环境中使用控制台输出日志更方便,这样可以通过持续集成 runner 执行 ...

  6. dotnet core TargetFramework 解析顺序测试

    dotnet core TargetFramework 解析顺序测试 Intro 现在 dotnet 的 TargetFramework 越来越多,抛开 .NET Framework 不谈,如果一个类 ...

  7. 2.10. 代码片段:demo方法(Core Data 应用程序实践指南)

    该代码段我觉得没有太多东西 - (void)applicationDidBecomeActive:(UIApplication *)application { [self cdh]; [self de ...

  8. 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 ...

  9. ASP.NET Core 集成测试中结合 WebApplicationFactory 使用 SQLite 内存数据库

    SQLite 内存数据库(in-memory database)的连接字符串是  Data Source=:memory: ,它的特点是数据库连接一关闭,数据库就会被删除.而使用  services. ...

随机推荐

  1. 如何手写JDK锁

    手写JDK锁 需要三个步骤: 手写一个类MyLock,实现Lock接口 重写lock()方法 重写unlock()方法 代码: public class MyLock implements Lock ...

  2. 学习笔记—CSS基础

    概念 CSS 指层叠样式表(Cascading Style Sheets), 是一种标记语言,属于浏览器解释型语言,可以直接由浏览器执行,不需要编译. HTML只负责网页的内容,标签尽量只负责语意不负 ...

  3. 关于RecyclerView嵌套导致item复用异常,界面异常的问题

    常规需求: 外层RecyclerView嵌套内层RecyclerView , 在上下滑动的时候会出现item数据以及view的显示异常. 解决办法: 1.重写  getItemViewType  方法 ...

  4. Android studio无法创建类和接口问题解决办法。提示 Unable to parse template "Class"

    重新配置了studio 的工作环境, 没问题,后来加需求要新增java类和接口,这就出现问题了,新建的时候,就会报错: Unable to parse template "Interface ...

  5. Windows系统ping本地虚拟机~

    虚拟机左上角[编辑]>>>[虚拟网络编辑器] [VMnet1]>>>子网ip:192.168.124.0 子网掩码:255.255.255.0 [VMnet8]&g ...

  6. Windows Server 2016-DNS客户端新增功能

    域名系统(DNS)是包含TCP / IP的行业标准协议套件之一,DNS客户端和DNS服务器一起为计算机和用户提供计算机名称到IP地址映射名称解析服务. 在Windows Server 2016中,DN ...

  7. CORS的简单理解

    去年我在做一个项目,是关于标签打印的,它就是一个Windows程序,提供标签打印功能,由其它程序(包括网站)告诉它需要打印怎样的标签,它就出标签,这个“告诉它需要怎样的标签”的过程,是通过HTTP的P ...

  8. Scanner对象及其获取数据出现小问题和解决方案

    Scanner类简介: Java 5添加了java.util.Scanner类,我们可以通过Scanner类来获取用户输入.它是以前的StringTokenizer和Matcher类之间的某种结合.由 ...

  9. 【ElasticSearch篇】--ElasticSearch从初识到安装和应用

    一.前述 ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口,在企业中全文搜索时,特别常用. 二.常用概念 clu ...

  10. Self Host 使用 Exceptionless 实时监控程序运行日志服务

    Exceptionless 是一个可以对 ASP.NET Core, ASP.NET MVC,WebAPI, WebForms, WPF, Console 应用提供系统的日志,错误监控.报表等服务实时 ...