【五分钟的dotnet】是一个利用您的碎片化时间来学习和丰富.net知识的博文系列。它所包含了.net体系中可能会涉及到的方方面面,好比C#的小细节,AspnetCore,微服务中的.net知识等等。git
经过本篇文章您将Get:github
.NET Core
应用添加单元测试.NET Core
应用进行代码覆盖率度量Azure Devops
进行自动化构建时长为大约有十分钟,内容丰富,建议先投币再上车观看😜shell
发现网上不多有讲解关于.NET Core
的单元测试
文章,代码覆盖率
的文章就更少了。因此就抽时间梳理了一篇😁。服务器
先来讲一下单元测试
,对于已经开始使用单元测试的小伙伴能够直接跳过这个小节。框架
那么我们为何须要进行单元测试呢?确定是为了减小错误和bug的发生呀,这个不用说你们都知道。网上也有不少介绍单元测试
的文章,可是大多都是从一个很简单的方法入手,好比下面这个方法:ide
public int SimpleMehtod(int a1, int a2) { return a1 + a2; }
而后告诉你们咱们须要对这个方法进行验证。 其实这种教程由潜入深,好是好,可是不少没有涉及过单元测试的小伙伴就会感到很懵逼:“这个代码这么简单,我为啥要单元测试?一眼就看出返回两个值的和”,这样反而不能更好的体现单元测试所带来的直观好处。微服务
因此,我们反过来,思考有下面的一个方法:工具
/// <summary> /// 获取某个类型的泛型参数 /// </summary> /// <param name="type">须要检测的类型</param> /// <param name="genericType">检测类型所继承的泛型接口</param> /// <returns>泛型接口的全部参数信息</returns> public Type[] MyDemoMethod(Type type, Type genericType) { return type.GetInterfaces() .Where(i => IsGenericType(i)) .SelectMany(i => i.GetGenericArguments()) .ToArray(); bool IsGenericType(Type type1) => type1.IsGenericType && type1.GetGenericTypeDefinition() == genericType; }
相对而言该方法就显得复杂一些,它的功能是返回一个类型所继承的泛型接口的全部参数。单元测试
假设咱们在一次功能迭代中,编写了这样一个MyDemoMethod
的方法,该方法很明显是做为一个工具方法来被其它调用者使用。那么,当咱们刚刚编写完这个方法的时候,咱们就很想知道这个方法是否是可以正确的执行怎么办呢?“编写一个控制台程序来测试?”、“等最后功能所有写完了再来看”、“无论了”。学习
在我们没有使用单元测试的时候,上面的几个操做是常见的状况,可能不少小伙伴会基于控制台来测试;还有一些小伙伴直接F5运行应用来进行测试,这样直接运行程序会花费咱们大量的琐碎时间(好比登陆,操做功能,进入模块,测试该功能…………);最后是那些“等最后功能所有写完了再来看”的朋友,若是记性还不错的话,最后还能够记得来要对这个功能测试,要是后期编写了不少其它业务功能的话,可能早都忘记有这个方法了,因此最后就是彻底“裸奔”。
因此,此时就须要我们引入“单元测试”了。当一个方法被多个地方使用,过早的对该方法进行单元测试
,将会大幅度的减小bug的产生。
在.NET Core
中使用单元测试也很简单,直接新建一个测试项目就能够了。本次文章选择的是基于Xunit
所创建的测试项目,而后在测试项目中引用须要测试的项目:
接下来您须要对您须要测试的类编写对应的测试用例。假如咱们编写了以下的方法(别问我为何不是上面的那个泛型基础方法,由于待会要测代码覆盖率,为了简单):
public int CalDemo(int s, bool checkSign = true) { if (s > 10 && checkSign) { return s << 2; } else { return s; } }
在实际项目中,咱们可能有许许多多像这样的方法。具备几个分支,每一个分支执行不一样的代码。针对该CalDemo
方法,很明显当传入参数s
大于10和小于10的时候有着不一样的执行逻辑(先忽略checkSign
参数),因此咱们能够分别测试当s大于10或者s小于等于10的状况:
在xunit
测试项目中编写如下用例:
[Fact] public void CalDemo_ArguementMoreThan10() { var testValue = 11; DemoClass demoClass = new DemoClass(); var result = demoClass.CalDemo(testValue); //判断结果是否等于 44。 若是是则测试经过 Assert.Equal(44, result); } [Fact] public void CalDemo_ArguementLessThan10() { var testValue = 9; DemoClass demoClass = new DemoClass(); var result = demoClass.CalDemo(testValue); //判断结果是否等于 9。 若是是则测试经过 Assert.Equal(9, result); }
这样咱们就完成了对该方法的测试,固然您还能够编写:“传入参数等于10”,“传入参数为空”,“传入参数为负数”等等用例。
在VS
中打开"测试资源管理器"来运行测试看看吧:
有关xunit
的使用,您能够参考:Getting Started with xUnit.net
经过“测试资源管理器”,咱们能够看到单元测试的正确与否。可是,我如何知道该单元的代码是否都测试完成了呢?若是没有完成我还须要编写哪些测试用例呢?
这个时候,咱们就须要对测试进行度量,度量哪些代码已经被咱们测试过,哪些代码没有被测试到。针对没有测试到的部分,咱们再编写一些Case进行测试。
因此咱们能够引入代码覆盖率
的概念来进行评估。关于该概念的内容我这里就不在过多阐述了,你们有兴趣能够“百度谷歌必应”三条龙服务。
在VS
中,为咱们提供了代码覆盖率的菜单项:在“测试” 菜单中,选择“分析全部测试的代码覆盖率” 。
经过该功能咱们就能够对已有的单元测试进行代码覆盖率度量。
是否是很简单? 可是你会发现:“你根本找不到这个按钮!!!!!!”。
别找了,您的Visual Studio 2019
没得这个菜单? 为何呢? 由于您没有充钱啊!!!
,该功能只针对Visual Studio Enterprise
(企业版)提供。使用社区版
的我,眼泪流下来。
你觉得这样就能难倒我了吗? 直接上开源的度量工具:coverlet。来看看关于Coverlet的介绍:“Coverlet是一个跨平台的.NET代码覆盖框架,支持行、分支和方法覆盖。它与Windows上的.NET Framework和全部受支持的平台上的.NET Core一块儿工做。”
这里我强烈推荐你们使用Coverlet来进行代码覆盖率测试,为何呢?由于它跨平台呀。后面咱们会使用Linux环境来进行自动化构建,因此Coverlet具备明显的优点,在Azure
的官方文档中也推荐你们使用Coverlet:
使用Coverlet
也很简单,直接在您的测试项目安装对应的Nuget
包依赖就能够了:
dotnet add package coverlet.collector
由于跨平台的特性,因此您可能已经想到了,我们接下来就没有像“测试资源管理器”那样的界面能够一键点击了,因此咱们得使用命令行的方式来进行操做,对于一些小伙伴可能须要习惯习惯。
在xunit
项目中执行如下命令:
dotnet test --collect:"XPlat Code Coverage"
我我的比较喜欢用powershell
来执行,固然您能够在vs中用程序包管理控制台来选中项目执行:
执行后您会发如今项目中多了一个叫作TestResults
的文件夹,该文件夹就是本次代码度量的结果:
可是您立刻又会发现一个问题,这个报告它喵的是xml格式,看起来十分费解。
因此,咱们又引入了另一个神器:ReportGenerator
。关于该工具的描述能够参考:ReportGenerator。 它的做用就像它的名字同样,就是为了生成代码覆盖的报告。
ReportGenerator提供了几种使用方式,一种是您经过Nuget
包来使用它,还有就是把他做为一个全局的命令行指令工具来安装它。 这里咱们选择了第二种,为了之后使用,因此选全局的来的爽。
在powershell
中执行下面命令:
dotnet tool install --global dotnet-reportgenerator-globaltool
而后就可使用它来生成报告了,仍是用powershell
在xunit
测试项目中执行下面的代码:
reportgenerator "-reports:**\coverage.cobertura.xml" "-targetdir:coveragereport"
这句话的意思是:根据将xunit
项目下的coverage.cobertura.xml
文件来生成报告,输出目录为coveragereport
。
执行以后,在目录中就会出现一个名为coveragereport
的文件夹,打开之后点击里面的Index文件打开网页。
哇,效果舒服多了:
这里您会看到有两个度量指标:一个叫作Line coverage
(语句覆盖),另外一个叫作Branch coverage
(分支覆盖率)。而后您能够点击我们的源代码文件进入,看看为何会有这样的结果:
红色的部分就是我们已经覆盖的语句,直观的就能看到咱们测试了哪些代码。而左侧箭头所标记的地方就是具备分支的地方,这个s > 10 && checkSing
就是一个明显的分支。
经过这种方式咱们就可以清楚而且直观的知道咱们的代码哪些完成了测试,哪些地方有遗漏等。
单元测试 + 代码覆盖率 的方式可以大幅度的减小咱们开发中隐藏的bug,特别是做为我的开发者来讲,由于没有专门的测试人员,因此须要本身检测本身的代码,纯靠肉眼来观察的话是很粗糙的,毕竟本身写的代码本身最难发现bug。所以假如时间容许,咱们应该尽量的引入单元测试
和代码覆盖率
。
通常来讲,编写单元测试会扩大代码量至3倍以上,因此这也是不少公司或者开发者选择放弃使用单元测试
的缘由。可是“出来混早晚是要还的”,假如是一个长期运行的项目,越早发现bug是越关键的一件事,这将关系到项目后期可否稳定运行下去。
注意!!!,哪怕代码覆盖率达到了100%,也不是证实项目就不会出现bug了。单元测试的全覆盖只能证实您的单元没有问题,需求理解错误或者功能集成时所致使的bug是不会在该阶段被发现的,所以咱们仍是须要进行其它的测试,好比集成测试,自动化接口测试等。
既然有了这么好的单元测试
和代码覆盖率
,那我确定但愿每次提交代码的时候就可以为此次的代码进行一次测试和反馈。因此我们可使用微软这些年吹爆了的Azure
平台,人人上云,云上两开花。
接下来,将展现如何利用Azure Devops
进行自动化构建。在这以前,我们先来看看微软为咱们开发者带来的一些福利:
关于自动化构建,您也能够选用Github Action
。你们都知道,自从Github
被归入到微软旗下以后,势头也是愈加的猛,如今Github teams
都直接免费了。再来看看Azure Devops
这边,假如是开源项目,直接无偿使用,就算是私有,每一个都有30个小时的使用时间。这两兄弟左右开弓,尔等小菜只能说一句“微软巨硬牛B”。
每一次自动化构建的Job背后都是使用云服务器的资源来进行构建,微软直接在Github和Azure这边提供了免费的资源来供您构建,配置好像仍是一台2核4G的主机。用老罗的话来讲,真的是“不赚钱,就交个朋友”。
因此要使用Azure Devops
的话,请先注册您的微软帐号。下面的演示我将代码托管在Github上,权限为公开,而后从Azure Devops
这边连接Github的库进行构建。
从Pipelines
中新建一个Pipelines:
我这里选择的是Github
的代码库,而后下一步进行选择,选择项会有几个模板供您选择,您能够随意选择一个AspNet Core
的模板,而后进行下一步进行配置。在下面的图片中,表示了一个对.NET Core
程序进行“自动生成->测试->生成代码覆盖率”的job,您能够根据您的自身状况进行参考和更改:
而后提交该配置。当master
分支的代码进行变更的时候,job就会自动执行,执行的结果能够在Pipelines
看到:
再来看看我们的代码度量结果:
完美。
不知道有没有人像同样,很喜欢点QQ图标之类的东西。(因此我在博客园添加了两个徽章😂)
固然,使用徽章的话可让用户一下就了解到项目的状况,好比版本号,下载数量,开源协议等等。
Azure Piplines
提供了一个徽章,您能够从job的右上角获取到:
该徽章是关于job的构建成功的信息。可是若是您想获取到其它的信息,可使用shields来进行获取:
打开shields的官网:“https://shields.io/category/downloads”。 选择您所须要添加的徽章类别,这里我们选择了Azure Coverage
:
进行输入对应信息后,就能够获取到刚才我们job中所获得的代码覆盖率的结果了。
而后…………选则一些您须要展现的信息,很快就累计了一排勋章了😂。
说几个你们可能在单元测试过程当中可能涉及到的几个小点:
internal
级别的类,可是当测试项目引用以后是没有办法找到该类的,您能够经过将程序集标记为对测试项目可见来进行测试:[assembly: InternalsVisibleTo("MyDemo.Tests")] namespace DuDuDu.MyDemo.Internals { internal class DefaultDemoClass { } }
2.若是有依赖注入怎么办? 好比我们测试AspNetCore的应用时,会有不少类实际上是被注入到了DI容器中,可是测试的类又依赖了这些类。
可使用ServiceCollection
来做为测试的容器实现,而后把涉及的服务都添加进去:
[Fact] public void Test() { IServiceCollection Services = new ServiceCollection(); Services.AddTransient<xx,xx>(); Services.AddTransient<xx,xx>(); Services.AddTransient<xx,xx>(); var provider = Services.BuildServiceProvider(); var testClass = provider.GetService<xx>(); testClass.TestMethod(); }
若是有依赖的类尚未实现的时候,能够经过Mock的方法来模拟一个接口完成操做。
3.若是项目多了的话,怎么执行测试和代码度量呢?
我如今选用的是使用Powershell
脚原本编写脚本完成的。开发的时候利用VS的“测试资源管理器”来进行单元测试,当单元测试验证的差很少的时候,使用“Powershell”脚原本进行代码覆盖率进行测试,查看忽略的代码而后继续测试。测试经过以后再提交代码到Github
,而后Azure Devops
进行构建。
好啦,今天的内容有些多,可是对您开发.NET Core
项目来讲的话,是实实在在的有用。
预告一下,下一期会为你们带来“对AspNet Core返回结果进行自动包装”的文章😄。
最后,偷偷说一句:创做不易,点个推荐吧.....