使用xUnit为.net core程序进行单元测试(上)

一. 导读

为什么要编写自动化测试程序(Automated Tests)?

  • 可以频繁的进行测试

  • 可以在任何时间进行测试,也可以按计划定时进行,例如:可以在半夜进行自动测试。

  • 肯定比人工测试要快。

  • 可以更快速的发现错误。

  • 基本上是非常可靠的。

  • 测试代码与生产代码紧密结合。

  • 使得开发团队更具有幸福感!

自动化测试的分类:

纵轴表示测试的深度,也就是说测试的细致程度。

横轴则表示测试的覆盖程度。

  • Unit Test 单元测试, 它可以测试一个类,或者一个类的某个功能,它具有很好的深度,但是对整个应用来说它不具备很好的覆盖面。

  • Integration Test 集成测试,它没有单元测试那么细致,但是具有相对较好的测试覆盖面。例如它可以测试功能的组合,以及像数据库或文件系统这样的外部资源等。

  • Subcutaneous Test 皮下测试,这种测试作用于UI层的下面一层,这也意味着它对整个应用来说有很好的覆盖率,但是深度欠佳。那一个MVC结构的应用来说,它就是针对刚好在Controller下面一层的测试,对于Web service来说,它就是对节点下面那层的测试。

  • UI测试,它的测试覆盖面很广,直接从UI层面进行测试,但是深度欠佳。

从速度来看 单元是最快的,而UI测试是最慢的。

从脆弱性来看 UI测试是最差的,程序修改后极有可能需要修改测试代码,而单元测试是最好的。

是测试行为还是测试私有方法(private method)?

public void IncreaseHeartBeatRate()

        {

            HeartBeatRate = CalculateHeartBeatRate() + 2;

        }


        private int CalculateHeartBeatRate()

        {

            var random = new Random();

            return random.Next(1, 100);

        }

大多数情况下单元测试都应该是针对类的行为进行测试的,也就是public方法。当然也纯在不同的观点。

如果想要对private方法进行测试的话,是有很多缺点的:

  • 首先需要修改方法的访问限制需要从private改为public,这就破坏了面向对象的封装性。

  • 再者,这其实测试的是类的具体实现细节,而不是类的行为。如果我们想要对类的内部进行重构的话,就会破坏测试,导致测试也必须重构。如果必须对private方法进行测试,那么首先建议您把private修饰符改成internal,然后修改该项目(project)的AssemblyInfo.cs,它在项目的Debug或者Release文件夹下。代码如下:

[assembly: InternalsVisibleTo("Hospital.Tests")]

这表示Hospital.Tests这个测试项目可以访问该项目生产代码(production code)的internal方法。

测试的三个阶段 AAA

  • Arrange,这里做一些先决的设定。例如创建对象实例,数据,输入等等。

  • Act,在这里执行生产代码并返回结果。例如调用方法,或者设置属性(Properties)。

  • Assert,在这里检查结果。测试通过或者失败。

xUnit.net

官网:https://xunit.github.io/

xUnit是一个测试框架,可以针对.net/core进行测试。

测试项目需引用被项目从而对其进行测试,测试项目同时需要引用xUnit库。测试编写好后,用Test Runner来运行测试。Test Runner可以读取测试代码,并且会知道我们所使用的测试框架,然后执行,并显示结果。目前可用的Test Runner包括vs自带的Test Explorer,或者dotnet core命令行,以及第三方工具,例如resharper等等。

xUnit支持的平台:

.net full, .net core, .net standard, uwp, xamarin.

xUnit的例子:

[Fact]

        public void TestIncreaseHeartBeatRate()

        {

            var patient = new Patient(); // Arrange

            patient.IncreaseHeartBeatRate(); // Act

            Assert.InRange(patient.HeartBeatRate, 40, 100); // Assert

        }

安装配置xUnit.net

a.使用Visual Studio 2017

首先建立一个C# library项目,叫Hospital(下面部分截图有个拼写错误,应该是Hospital),然后建立一个xUnit Test项目,叫Hospital.Tests:

 

可以看到Hospital.Tests已经包含里这几个库:

然后为Hospital.Tests添加到Hospital项目的引用。

b.使用.net core 命令行

首先把刚才建立的Hospital.Tests项目移除(目录需要手动删除).

然后打开项目位置:

按住shift打开命令行:

用命令行创建项目:

创建 Hospital.Tests目录,进入目录,使用命令dotnet new xunit创建xUnit单元测试项目。

添加项目的引用:

最后添加项目到解决方案:

回到VS界面,提示重新加载:

确认后,VS中解决方案结构如:

做第一个测试

对测试项目的文件名进行一些重构,编写以下代码,并进行Build:

从Test Explorer我们可以看到一个待测试的项目。

在这里,我们可以对测试项目进行分组和排序,如图:

想要运行所有的测试,就点击上面的Run All按钮。如果像运行单个测试,那么右击选择Run Selected Tests:

运行后,可以看到结果,Passed:

我们同样可以通过命令行来进行测试:

进入到Tests目录,执行 dotnet test命令,所有的测试都会被发现,然后被执行:

因为我们并没有在测试方法中写任何的Assert,所以测试肯定是通过的,但这个测试也是个无效的测试。

Assert

Assert做什么?Assert基于代码的返回值、对象的最终状态、事件是否发生等情况来评估测试的结果。Assert的结果可能是Pass或者Fail。如果所有的asserts都pass了,那么整个测试就pass了;如果有任何assert fail了,那么测试就fail了。

xUnit提供了以下类型的Assert:

  • boolean:True/False

  • String:相等/不等,是否为空,以..开始/结束,是否包含子字符串,匹配正则表达式

  • 数值型:相等/不等,是否在某个范围内,浮点的精度

  • Collection:内容是否相等,是否包含某个元素,是否包含满足某种条件(predicate)的元素,是否所有的元素都满足某个assert

  • Raised events:Custom events,Framework events(例如:PropertyChanged)

  • Object Type:是否是某种类型,是否某种类型或继承与某种类型

一个test里应该有多少个asserts?

一种建议的做法是,每个test方法里面只有一个assert。

而还有一种建议就是,每个test里面可以有多个asserts,只要这些asserts都是针对同一个行为就行。

第一个Assert

目标类:

public class Patient

    {

        public Patient()

        {

            IsNew = true;

        }


        public string FirstName { get; set; }

        public string LastName { get; set; }

        public string FullName => $"{FirstName} {LastName}";

        public int HeartBeatRate { get; set; }

        public bool IsNew { get; set; }


        public void IncreaseHeartBeatRate()

        {

            HeartBeatRate = CalculateHeartBeatRate() + 2;

        }


        private int CalculateHeartBeatRate()

        {

            var random = new Random();

            return random.Next(1, 100);

        }

    }

测试类:

public class PatientShould

    {

        [Fact]

        public void HaveHeartBeatWhenNew()

        {

            var patient = new Patient();


            Assert.True(patient.IsNew);

        }

    }

运行测试:

 

结果符合预期,测试通过。

改为Assert.False()的话:

测试Fail。

String Assert

测试string是否相等

       [Fact]

        public void CalculateFullName()

        {

            var p = new Patient

            {

                FirstName = "Nick",

                LastName = "Carter"

            };

            Assert.Equal("Nick Carter", p.FullName);

        }

然后你需要Build一下,这样VS Test Explorer才能发现新的test。

运行测试,结果Pass:

同样改一下Patient类(别忘了Build一下),让结果失败:

从失败信息可以看到期待值和实际值。

StartsWith, EndsWith

[Fact]

        public void CalculateFullNameStartsWithFirstName()

        {

            var p = new Patient

            {

                FirstName = "Nick",

                LastName = "Carter"

            };

            Assert.StartsWith("Nick", p.FullName);

        }


        [Fact]

        public void CalculateFullNameEndsWithFirstName()

        {

            var p = new Patient

            {

                FirstName = "Nick",

                LastName = "Carter"

            };

            Assert.EndsWith("Carter", p.FullName);e);

        }

Build,然后Run Test,结果Pass:

忽略大小写 ignoreCase

string默认的Assert是区分大小写的,这样就会失败:

可以为这些方法添加一个参数ignoreCase设置为true,就会忽略大小写:

包含子字符串 Contains

       [Fact]

        public void CalculateFullNameSubstring()

        {

            var p = new Patient

            {

                FirstName = "Nick",

                LastName = "Carter"

            };

            Assert.Contains("ck Ca", p.FullName);

        }

Build,测试结果Pass。

正则表达式,Matches

测试一下First name和Last name的首字母是不是大写的:

[Fact]

        public void CalculcateFullNameWithTitleCase()

        {

            var p = new Patient

            {

                FirstName = "Nick",

                LastName = "Carter"

            };

            Assert.Matches("[A-Z]{1}{a-z}+ [A-Z]{1}[a-z]+", p.FullName);

        }

Build,测试通过。

数值 Assert

首先为Patient类添加一个property: BloodSugar。

public class Patient

    {

        public Patient()

        {

            IsNew = true;

            _bloodSugar = 5.0f;

        }


        private float _bloodSugar;

        public float BloodSugar

        {

            get { return _bloodSugar; }

            set { _bloodSugar = value; }

        }

        ...

Equal:


        [Fact]

        public void BloodSugarStartWithDefaultValue()

        {

            var p = new Patient();

            Assert.Equal(5.0, p.BloodSugar);

        }


Build,测试通过。

范围, InRange:

首先为Patient类添加一个方法,病人吃饭之后血糖升高:

        public void HaveDinner(){          
var random = new Random();_bloodSugar += (float)random.Next(1, 1000) / 100; //  应该是1000}

添加test:


          [Fact]

        public void BloodSugarIncreaseAfterDinner()

        {

            var p = new Patient();

            p.HaveDinner();

            // Assert.InRange<float>(p.BloodSugar, 5, 6);

            Assert.InRange(p.BloodSugar, 5, 6);

        }

Build,Run Test,结果Fail:

可以看到期待的Range和实际的值,这样很好。如果你使用Assert.True(xx >= 5 && xx <= 6)的话,错误信息只能显示True或者False。

因为HaveDinner方法里,表达式的分母应该是1000,修改后,Build,Run,测试Pass。

原文地址:https://www.cnblogs.com/cgzl/p/8283610.html


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/322325.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

select2删除选中项,allowClear设置

转载自 select2删除选中项&#xff0c;allowClear设置 在使用select2过程中&#xff0c;有时候需要删除我们选中的选项&#xff0c;如下图&#xff1a; 这时候就需要设置select2的allowClear属性了。 有两种方法&#xff1a; 第一种&#xff1a; 直接用select2定义的一个c…

LeetCode算法总结-回溯法与深度优先搜索

转载自 LeetCode算法总结-回溯法与深度优先搜索 回溯法&#xff08;探索与回溯法&#xff09;是一种选优搜索法&#xff0c;又称为试探法&#xff0c;按选优条件向前搜索&#xff0c;以达到目标。但当探索到某一步时&#xff0c;发现原先选择并不优或达不到目标&#xff0c;就…

入门干货之用DVG打造你的项目主页-Docfx、Vs、Github

由于这三项技术涉及到的要点以及内容较多&#xff0c;希望大家有空能自己挖掘一下更多更深的用法。0x01、介绍VS&#xff0c;即VS2017以及以上版本&#xff0c;宇宙最好的IDE&#xff0c;集成了宇宙最有前景的平台&#xff0c;前阶段也支持了宇宙最好的语言。Github&#xff0c…

ASP.NET Core中使用IOC三部曲(一.使用ASP.NET Core自带的IOC容器)

前言本文主要是详解一下在ASP.NET Core中,自带的IOC容器相关的使用方式和注入类型的生命周期.这里就不详细的赘述IOC是什么 以及DI是什么了.. emm..不懂的可以自行百度.正文今天我们主要讲讲如何使用自带IOC容器,emm..虽然自带的功能不是那么强大,但是胜在轻量级..而且..不用引…

P4130,jzoj1214-[NOI2007]项链工厂【线段树】

正题 题目链接:https://www.luogu.org/problemnew/show/P4130 题目大意 一个环形颜色珠子链&#xff0c;位置(注意不是上面的珠子)从最上顺时针下来位置依次标号1∼n1\sim n1∼n。 然后要求支持以下操作 Rk:R\ k:R k:将所有珠子顺时针旋转kkk个。F:F:F:将所有珠子以111向下翻…

LeetCode常用算法模式大厂面试题整理

转载自 LeetCode常用算法模式&大厂面试题整理 文章目录 1、滑动窗口 2、双指针 3、快慢指针 4、合并区间 5、循环排序 6、就地反转链表 7、堆-优先队列问题 8、Top K 9、归并 10、单调栈 11、回溯法 BATJ等大厂面试真题汇总 1、滑动窗口 1 一个左指针&#xff0c;一个右…

ABPZero系列教程之拼多多卖家工具

此系列文章围绕着拼多多卖家工具来介绍ABPZero的使用&#xff0c;内容包括手机登录、手机注册、拼团提醒、微信公众号绑定帐号、有拼团发送消息到微信公众号&#xff08;只要关注过微信公众号并已绑定系统帐号&#xff09;。学习此系列必备&#xff1a;手机验证码&#xff1a;使…

g4e基础篇#4 了解Git存储库(Repo)

Git 存储库看上去就是一个文件夹&#xff0c;只是在这个文件夹中不仅仅保存了所有文件的当前版本&#xff0c;也同时保存了所有的历史记录&#xff0c;这些额外的信息都保存在当前文件夹下面的.git子目录中。因为前面我们所描述的git跟踪改动的特殊方式 &#xff0c;git可以在很…

net的retrofit--WebApiClient库

# 库简介WebApiClient是开源在github上的一个httpClient客户端库&#xff0c;内部基于HttpClient开发&#xff0c;是一个只需要定义c#接口(interface)&#xff0c;并打上相关特性&#xff0c;即可异步调用http-api的框架 &#xff0c;支持.net framework4.5、netcoreapp2.0和ne…

Sentinel(一)之简介

转载自 Sentinel: 分布式系统的流量防卫兵 Sentinel 是什么&#xff1f; 随着微服务的流行&#xff0c;服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量控制组件&#xff0c;主要以流量为切入点&#xff0c;从限流、流量整形、熔断降级、系统负…

使用xUnit为.net core程序进行单元测试(中)

第一部分: 使用xUnit为.net core程序进行单元测试(上), 下面有一点点内容是重叠的....String Assert测试string是否相等&#xff1a;[Fact]public void CalculateFullName(){var p new Patient{FirstName "Nick",LastName "Carter"};Assert.Equal(&quo…

Sentinel(二)之Quick Start

转载自 Sentinel Quick Start 1.1 公网 Demo 如果希望最快的了解 Sentinel 在做什么&#xff0c;我们可以通过 Sentinel 新手指南 来运行一个例子&#xff0c;并且能在云上控制台上看到最直观的监控和流控效果等。 1.2 手动接入 Sentinel 以及控制台 下面的例子将展示应用如…

.net的retrofit--WebApiClient库深入篇

前言本篇文章的内容是对上一篇.net的retrofit--WebApiClient库的深层次补充&#xff0c;你可能需要先阅读上一篇才能理解此篇文章。本文将详细地讲解WebApiClient的原理&#xff0c;结合实际项目中可能遇到的问题进行使用说明。库简介WebApiClient是开源在github上的一个httpCl…

Sentinel(三)之如何使用

转载自 Sentinel如何使用 简介 Sentinel 可以简单的分为 Sentinel 核心库和 Dashboard。核心库不依赖 Dashboard&#xff0c;但是结合 Dashboard 可以取得最好的效果。 这篇文章主要介绍 Sentinel 核心库的使用。如果希望有一个最快最直接的了解&#xff0c;可以参考 新手指…

ASP.NET Core中使用IOC三部曲(二.采用Autofac来替换IOC容器,并实现属性注入)

上一篇ASP.NET Core中使用IOC三部曲(一.使用ASP.NET Core自带的IOC容器) ,我们说过ASP.NET Core中自带的IOC容器是属于轻量级的,功能并不是很多,只是提供了基础功能而已..所以今天我们主要讲讲如何采用Autofac来替换IOC容器,并实现属性注入注意:本文需要读者理解DI IOC并使用过…

Sentinel(四)之工作主流程

转载自 工作主流程 Overview 在 Sentinel 里面&#xff0c;所有的资源都对应一个资源名称&#xff08;resourceName&#xff09;&#xff0c;每次资源调用都会创建一个 Entry 对象。Entry 可以通过对主流框架的适配自动创建&#xff0c;也可以通过注解的方式或调用 SphU API…

在 .NET Core 中的并发编程

原文地址:http://www.dotnetcurry.com/dotnet/1360/concurrent-programming-dotnet-core今天我们购买的每台电脑都有一个多核心的 CPU&#xff0c;允许它并行执行多个指令。操作系统通过将进程调度到不同的内核来发挥这个结构的优点。然而&#xff0c;还可以通过异步 I/O 操作和…

Sentinel(五)之流量控制

转载自 流量控制 概述 流量控制&#xff08;flow control&#xff09;&#xff0c;其原理是监控应用流量的 QPS 或并发线程数等指标&#xff0c;当达到指定的阈值时对流量进行控制&#xff0c;以避免被瞬时的流量高峰冲垮&#xff0c;从而保障应用的高可用性。 FlowSlot 会…

ASP.NET Core中使用IOC三部曲(三.采用替换后的Autofac来实现AOP拦截)

上一篇ASP.NET Core中使用IOC三部曲(二.采用Autofac来替换IOC容器,并实现属性注入)我们讲了如何将默认的容器替换为Autofac,并使用属性注入.那么这一篇我们就来讲讲如何利用Autofac实现我们的AOP(面向切面编程) .1.引用正确的库来实现AOP既然是跨平台,那么在asp.net core因为采…