C语言[转]前后端分离开辟格局下后端品质的承接保险 —— 单元测试

 依懒于接口/抽象,而非落成 

  那点本身想也就不需求细述了,在单元测试这几个情景之中。我们任重先生而道远是将业务与非业务相关功功效接口隔绝开,那么大家在单元测试中就足以很灵活的用Mock大概Stub来替换。比如:读写文件、访问数据库、远程请求等等。

单元测试与测试驱动开荒(TDD)

  测试驱动开拓其实大家用2个题材就足以解释清楚,那正是“你什么日期写单元测试?”
有人采用在开拓的代码写完今后再写,那样大家的支付进度是:
了解须求-》编写代码-》针对代码结合必要写单元测试。后来我们发现,往往在写单元测试的时候发现本人有些须要未有清楚通晓,或然那一个要求原本陈设的时候就一贯不思索到,所以又再一次改原来的代码。
于是有人就说,为何大家不干脆反过来? 先写单元测试,再写代码?
 所以我们开辟的经过就产生了这么:通晓须求-》针对要求写单元测试 -》
编写代码让单元测试通过。 最开首是叫测试先行(TFD: Test First
Development) ,后来就向上成大家熟识的”测试驱动开采”了。

  测试驱动开垦最大的功利是,让开辟人士越来越好的精通供给,甚至是打通要求之后再拓张开拓。
当然,大家不容许叁次性把全体的测试代码都写出来之后再写代码,那是叁个双重迭代的长河:

C语言 1

  由于TDD不是大家本篇的严重性内容,那里只有望能给大家2个对TDD的易懂认识的同时询问到TDD与单元测试的沟通。到此处,大家对此单元测试的定义就介绍的多数了,接下去是代码时间。:)
大家来上一个实打实的例证更形象的询问一下单元测试。

1个单元测试的例子

  那么难点来了,我们用什么样来案例来写了一个单元测试的事例吗?既然那样,那么我们就用前两篇我们在圈子模型驱动设计中讲到的用户注册的事例吗。在用户的领域服务中,UserService提供了1个Register的章程,通过用户名、邮箱和密码八个参数来创制3个用户的指标。
像全部注册逻辑同样,邮箱是不可能重复的,那是我们明天以此小圈子服务中比较重大的政工逻辑,所以大家的单元测试必需要覆盖到。
大家的测试

// UserServiceTests.cs

C语言 2C语言 3

C语言 4😉

 1 namespace RepositoryAndEf.Domain.Tests
 2 {
 3     public class UserServiceTests
 4     {
 5         private IRepository<User> _userRepository = new MockRepository<User>();
 6 
 7         [Fact]
 8         public void RegisterUser_ExpectedParameters_Success()
 9         {
10             var userService = new UserService(_userRepository);
11             var registeredUser = userService.Register(
12                 "hellojesseliu@outlook.com",
13                 "Jesse",
14                 "Jesse");
15 
16             var userFromRepository = _userRepository.GetById(registeredUser.Id);
17 
18             userFromRepository.Should().NotBe(null);
19             userFromRepository.Email.Should().Be("hellojesseliu@outlook.com");
20             userFromRepository.Name.Should().Be("Jesse");
21             userFromRepository.Password.Should().Be("Jesse");
22         }
23 
24         [Fact]
25         public void RegisterUser_ExistedEmail_ThrowException()
26         {
27             var userService = new UserService(_userRepository);
28             var registeredUser = userService.Register(
29                 "hellojesseliu@outlook.com",
30                 "Jesse",
31                 "Jesse");
32 
33             var userFromRepository = _userRepository.GetById(registeredUser.Id);
34             userFromRepository.Should().NotBe(null);
35 
36             Action action = () => userService.Register(
37                 "hellojesseliu@outlook.com",
38                 "Jesse_01",
39                 "Jesse");
40             action.ShouldThrow<ArgumentException>();
41         }
42 
43         public void RegisterUser_ExistedName_ThrowException()
44         {
45             var userService = new UserService(_userRepository);
46             var registeredUser = userService.Register(
47                 "hellojesseliu@outlook.com",
48                 "Jesse",
49                 "Jesse");
50 
51             var userFromRepository = _userRepository.GetById(registeredUser.Id);
52             userFromRepository.Should().NotBe(null);
53 
54             Action action = () => userService.Register(
55                 "hellojesseliu_02@outlook.com",
56                 "Jesse",
57                 "Jesse");
58             action.ShouldThrow<ArgumentException>();
59         }
60 
61     }
62 }

C语言 5😉

View
Code

   在那么些事例中我们用到了
Fluentassertions、XUnit那多少个开源组件。别的Moq作为多个科学的单元测试Mock框架也引入给我们。

  • Fluentassertions:绝对于.NET测试工具本人提供的Assert,Fluentassertions提供基于链式创设的某些更人性、易懂的措施来扶持写出越来越好明白的单元测试代码

    上边代码中大家所用到的ShoudBe、NotBe、以及ShoudThrow等措施即来自于Fluentassertions,还有越来越多形式能够到法定文书档案上查询。
  • Xunit:这是3个开源的单元测试工具
  • Moq:为了让单元测试能够完全剥离外部组件,大家须求运用一些Mock对象和Stub对象,而Moq是三个开源的Mock类框架能够扶持我们兑现这一个意义
    。大家地点代码中用到的MockRepository是大家团结用List封装的3个IRepository实例,协助增加和删除改查,也就是我们把数据持久化于内部存储器中。

C语言 6C语言 7

C语言 8😉

 1 namespace RepositoryAndEf.Data
 2 {
 3     public class MockRepository<T> : IRepository<T> where T : BaseEntity
 4     {
 5         private List<T> _list = new List<T>();
 6 
 7         public T GetById(Guid id)
 8         {
 9             return _list.FirstOrDefault(e => e.Id == id);
10         }
11 
12         public IEnumerable<T> Get(Expression<Func<T, bool>> predicate)
13         {
14             return _list.Where(predicate.Compile());
15         }
16 
17         public bool Insert(T entity)
18         {
19             if (GetById(entity.Id) != null)
20             {
21                 throw new InvalidCastException("The id has already existed");
22             }
23 
24             _list.Add(entity);
25             return true;
26         }
27 
28         public bool Update(T entity)
29         {
30             var existingEntity = GetById(entity.Id);
31             if (existingEntity == null)
32             {
33                 throw new InvalidCastException("Cannot find the entity.");
34             }
35 
36             existingEntity = entity;
37             return true;
38         }
39 
40         public bool Delete(T entity)
41         {
42             var existingEntity = GetById(entity.Id);
43             if (existingEntity == null)
44             {
45                 throw new InvalidCastException("Cannot find the entity.");
46             }
47 
48             _list.Remove(entity);
49             return true;
50         }
51     }
52 }

C语言 9😉

MockRepository.cs

   我们也得以用Moq框架在单元测试中一时半刻开始化二个MockRepository

C语言 10C语言 11

C语言 12😉

 1 private readonly IRepository<User> _userRepository;
 2         private List<User> _userList = new List<User>();
 3         public UserServiceTests()
 4         {
 5             var mockRepository = new Mock<IRepository<User>>();
 6 
 7             // 初始化新增方法 
 8             mockRepository.Setup(r => r.Insert(It.IsAny<User>())).Returns((User user) =>
 9             {
10                 if (_userList.Any(u => u.Id == user.Id))
11                 {
12                     throw new InvalidCastException("The id has already existed");
13                 }
14 
15                 _userList.Add(user);
16                 return true;
17             });
18 
19             _userRepository = mockRepository.Object;
20         }

C语言 13😉

View
Code

  在单元测试代码中一时半刻开首化Mock
repository

  • 越来越灵敏:能够只开始化用到的艺术 
  • 更加强的调整工夫:能够从外表(单元测试代码内)定义全部的作为 
  • 多态性:与其余单元测试类隔开,能够有差异的行为

 

自动化——持续集成

  持续集成里面早已包括了单元测试的自动化。它提倡团队开采成员必须平常集成他们的工作,甚至每一日都也许产生高频并入。而每便的集达卡以通过自动化的创设来评释,包涵电动编写翻译、发布和测试,从而尽快地窥见集成错误,让团队能够越来越快的开支援内地建设聚的软件。感兴趣的同班能够自动通晓,那是多个有关DevOps的话题,就不在本文作过多的表述。光想象一下那种不管什么人有代码check
in都引发全部单元测试代码的机关运营,在单元测试覆盖的全的状态下中央得以过滤掉大多的潜在bug。 

测试用例都有啥?

  写单元测试的代码或者是支付的某个倍,那句话是真的!在于你的单元测试用例覆盖的有多广,比如说大家地宗旨对用户注册那三个职业场景写了贰个测试用例,其实是远远不够的。

非预期的用例

  无论大家地点十分完全成功注册的用例,依然其它五个由于邮箱和称号再次而并未有挂号成功的用例。那七个用户都以预料的,假设是非预期的,比如:

  • 假设邮箱地址不是1个不错格式的信箱?
  • 比方本身邮箱不填?用户名不填?

边界测试

  • 只要本身的邮箱名称可能用户名长度超过最大范围?

回归测试

  修改bug是1件痛楚的事务,在错综复杂且耦合度极高的种类下修改bug是一件难受且胆破心惊的事体,那么您感受一下:在纷纷且耦合度异常高的种类下不断的修改同3个bug会是一种什么的心理。我们中期维护代码的时候对于新扩大的改造也需求加上对应的测试代码来保障单元测试的完整性。

何以才算好的单元测试?

怎么样是一个好的单元测试?

  • 是自动化的和可另行运营的
  • 很轻易实现
  • 连发有用
  • 任什么人1旦轻易的点一下开关就足以运作
  • 运维不会花太长的时光
  • 一向重返一样的结果(借使您不退换任何代码或参数)
  • 单元测试是一心割裂的,不该有别的其余的依懒
  • 当单元测试失利的时候,应该一眼就来看是因为何来头导致的那个失败
  • 2个测试方法只验证一个case,只用五个Mock,Stub能够是多个
  • 好的命名,最好是足以从事艺术工作术名看出以下五个因素(所以一般我们使用叁段命名法):
    • 测试指标
    • 条件 
    • 有道是赢得的结果

想知道您写的单元测试是或不是好的单元测试么?

  • 1个礼拜,大概三个月竟然贰年前写的单元测试还是可以运作并且赢得壹致的结果么?
  • 团队中的别的人也足以运作你贰个月前写的单元测试么?
  • 能够点击一下按键就运转你抱有的单元测试,并赶回正确的结果么?
  • 有着的单元测试能够在几分钟之内达成么?

    C语言 14

哪些是单元测试?

  有人可能写过单元测试,然则却不晓得怎么要写单元测试,有人掌握怎么要写单元测试,但不显著怎么样写才是好的单元测试。可是对于“测试”
大家各类人都如数家珍, 你看看上边包车型地铁机能是还是不是似曾相识?

C语言 15

概述

  在明日,
前后端分离已经是首推的三个开销情势。那对于后端团队来讲实在是1个好新闻,减轻义务并且更加小心。在测试方面,就更为依懒于单元测试对于API以及后端业务逻辑的较验。当然单元测试并非在内外端分离流行之后才有,它很已经存在,只是鲜有人注重且的确能够用好它。而在前后端分离开拓格局下,越发是双方交付时间差别异常的大的意况时,后端可能必要更为地依懒于单元测试来确定保证代码的科学。

  本文首要围绕单元测试张开,从单元测试的底蕴概念聊到,相比较单元测试和购并测试,同时大家还会聊一聊单元测试与测试驱动开采的区分。在大家询问完单元测试的定义之后,大家会追究一下什么的单元测试算得上是好的单元测试,它们持有哪些特征,怎么着运用隔绝框架来援助我们对一部分参差不齐的机件进行测试。最终四个剧情也是本文想要解说的主要性:
单元测试是开垦人士写的,那么开采人士在写自个儿的代码的时候,怎样提升自个儿代码的可测试性?
什么样的代码算的上是对单元测试友好的代码?
带着这么些标题,咱们那就来起先大家的单元测试之旅。

单元测试与测试

  测试项目分为很各类:单元测试、集成测试、系统一测试试、压力测试、负载测试、验收测试等等
,我们明日不打算也无法张开系统性的牵线。作为开辟人士,大家日常所说的“测试”。也正是说你代码写完了,老大问你测试通过了呢?你说过了,然后就足以Check
in
代码了。那里的“测试”,实际上指的是不完全的成效测试。为何说它不完整,是因为从专业测试的角度来讲,还亟需定义规范的测试用例,用例写完将来还要支付和测试职员一同评定审查等等 。
而我辈只是在脑海中预想了一下它应当咋做事的,应该给本身如何结果等,然后运维一下,咦,还真是那样的,那大家的测试固然通过了。
会有多少Bug,就在于大家这么些预想有多细了,往往有时大家只可以想到很少一部份,那时候专业独立的测试职员就派上用场了。同时掌握开辟和测试的人是很有优势的,自身能够保险写出来的软件的身分,那也是今世高效开垦公司所追求的,可是那样的人总是少之又少。

  单元测试是通过把三个应用程序拆分成可测试的10足小的一部分,然后把每壹有些与其他具备效率隔开开,单独对那1局地开始展览测试。而以此“可测试的足够小的部分”就叫做“单元“,在C语言中1个单元能够是贰个函数,在C#中单元测试能够是一个类。
如若持有的单元都能够像大家所预期的例行干活,那么把她们统一齐来就可以保障最少不会产出非常惨重的谬误。

正文转自:http://www.cnblogs.com/jesse2013/p/magic-of-unittesting.html\#3451709

最后

   编写单元测试就算简易,然则考验的却是细心和对工作的领会程度。而且往往写单元测试代码所花的岁月比写成效代码还要多,在职分时间进程紧、又不受珍重的场馆下,自身很少有人会积极愿意去写。然而,好的单元测试代码确实在长远能够体现出它的价值。

作者:Jesse 出处:http://jesse2013.cnblogs.com/

正文版权归笔者和天涯论坛共有,欢迎转发,但未经小编同意必须保留此段注解,且在篇章页面显著位置给出原来的书文连接,不然保留追究法律权利的职务。要是认为还有辅助的话,能够点一下右下角的【推荐】),希望能够持续的为大家带来好的技艺小说!想跟本人一块儿进步么?那就【关注】)我吧。

一体化架构层面包车型客车驰念

  假诺我们前天是重复开头搭建一套系统,那我们得以什么初始?或然说假使大家有气魄和决心去重构1套系统,我们该往哪些方向去走?——
从DDD的分段架构聊到

    分层
首先是由此分层把事情与任何基础零部件隔开分离开,不要让部分发邮件、记日志、写文件等这么些基础零部件混合了我们的政工,在应用层将世界工作与那个为应用服务的基础成效结合起来。在事先的一篇文章《初探领域驱动设计——为复杂性工作而生》有切实可行的牵线。

    C语言 16

  世界工作层无依懒

  在洋葱架构中,宗旨(Core)层是与世界或才具非亲非故的根基构件块,它包括了有的通用的构件块,例如list、case类或Actor等等。核心层不分包别的本领层面的定义,例如REST或数据库等等。 

  C语言 17

  假设有依懒,请依懒于接口抽象,而非具体的落到实处,比如我们例子中的IRepository。那么些架构观念实际已经很老很老了,不过我们超过五成的花色还停留在更更老的三层架构思想上,说好的手艺极客们都去哪个地方了?

Mock和Stub的区别

  因为有众多测试框架把Mock和Stub差异对待,初学者也会对那七个概念表示含糊不清。简单的来讲,Mock与
Stub最大的区分是:

  Stub主要用来隔开分离其余的组件让单元测试可以平常的进行,大家不会对Stub来进展Assert。

       C语言 18

  Mock则用来和测试代码进行互相,能够说我们会指向Mock来写测试代码,也会对它进行Assert来声明大家的代码。

  在大家地点的代码中,大家只用到了三个Mock(MockRepository),纵然同样是用户注册的事务,有哪些地点是大家兴许须要用到Stub的?
试想转手现实的注册场景,尽管用户注册成功了,
大家是还是不是索要给用户发送注册成功的邮件通知?那里有好几亟待小心的是,注册用户相关的代码属于大家圈子服务的职分,不过注册成功发送邮件、发送短信、甚至你要干一些体系有关的开端化操作都是属于应用层的事体。关于那一点,大家还足以回顾此前的两篇有关DDD的篇章。假使大家针对应用层的代码编写单元测试,那么大家就须求把某些零件比如邮件、日志等用Stub隔绝掉,来担保测试代码的周转。

增加代码的可测试性

  我们超过5八%遭遇的品类之具备很少看到单元测试的代码大约是因为以下的多少个原因:

  • 领导者不重视 ,团队内并未有这些风气
  • 类型太紧,根本不给时间(恐怕也有监护人不重申的原由)
  • 开辟人员对于单元测试不熟悉,不知晓什么写好单测试。(不佳的单元测试代码,写了说不定也正是白写,因为从来没人去运作它们)
  • 消除方案里面包车型客车事务层根本未曾主意写单元测试(耦合度太高,重依懒,那是当本身清除前面一个艰难之后,平日境遇的末段一道坎)

  关于最后一点是内需架构师、或许正如有经验在开采者在最早先陈设系统结构的时候要求思念到的。假使最开始并未有思虑到如何是好?
那太好了,因为不少品种最初始都并未有思考到,所以大家的单元测试代码总是盛行不起来。(可怜那一层面包车型大巴架构师也是少之又少,倒是有大多架构师活跃于各大论坛讲高产出、各类分布式组件,能挽起袖子去重构/优化代码结构的人真的少之又少。因为实际太累,而且搞倒霉还轻易出错,属于最有挑衅,但骨子里却频仍不被首席营业官青眼的一项苦差事)碰到相比较多的主题素材(包罗BAT等级的品种,只怕外面包车型客车架子、全部架构图画出来这是分外的精美,不过假诺涉及到职业规模的代码….前边作者就背着了。)

单元测试与集成测试 

  
为何要把这两项拿出来相比较,是因为那两项很轻巧模糊,1不小心你就大概把单元测试写成集成测试了,那也是为什么单元测试有时候看起来那么不佳的关键缘由。大家地点说单元测试是把每二个单元孤立出来,在测试的时候不能够和其余别的的单元有别的联系,那是单元测试,反过来你借使在您的测试代码中引进了别的三个单元,那你将在起先小心,你是还是不是早就上马写集成测试了。
当然有时往往不是引进了其余的1部分单元,有希望是壹些零部件,上面列出了壹部分单元测试和合并测试的首要特点,希望能够扶助大家区分单元测试与集成测试。

单元测试

  • 可另行运维的
  • 没完没了长时间有效,并且重回一致的结果
  • 在内部存款和储蓄器中运营,未有外部依懒组件(比如说真实的数据库,真实的文书存款和储蓄等)
  • 快快回到结果
  • 一个测试方法只测试2个难点

合龙测试

  • 动用真实的表面依懒(采用真实的数据库,外部的Web
    Service,文件存款和储蓄系统等)
  • 在二个测试之中大概会多个难点(数据库平常确,配置,系统逻辑等)
  • 能够在运营较长期之后才回到测试结果

保证类的引用/依懒关系清晰,可注入

  并非选用静态方案

  且毫无说有些面向对象的风味未有主意使用到,一旦开了这几个口子。天知道你的代码里面会依懒于有个别个外表静态方法,并且完全未有办法在测试代码大校它们mock掉,万1您在静态方法里面又有其它依懒,那对于单元测试来说正是一场完工。

  维持二个类具有的外部引用易见

  壹.  具有外部引用易见   二.  外部引用可注入/替换

  C语言 19

  除了构造函数注入以外,咱们还是可以运用构造函数注入、字段、以及艺术注入的主意,将大家的秘技替换掉。那种措施不仅是对单元测试友好,更是一种突出的代码社团办公室法,是大概提供代码的易读性,以及可维护性的。要清楚代码首固然给人观望的,只是有时让机器实行一下。假使有跳槽经验的同室应该都有过那种到了三个厂商,有1个很复杂的种类,不过并未有其余的文书档案(稍微好一点的或然会有表字典)的感受,唯一领会系统职业的点子是play
with the system 然后,看代码。
对于种不可能一眼看出种种类之间的涉嫌的代码,特别是3个类里面有好几百个章程、上万行代码的时候, 固然自个儿对此干那种业务已经熟练,但随即的心态难免依旧有点激(操)动(蛋)的。

目录

  1. 怎么样是单元测试?
    1. 单元测试与测试
    2. 单元测试与集成测试
    3. 单元测试与测试驱动开荒
    4. 多少个单元测试的事例
    5. Mock和Stub的区别
  2. 怎么着才算好的单元测试?
    1. 测试用例都有何?
    2. 自动化——持续集成
  3. 拉长代码的可测试性
    1. 完全框架结构层面的设想
    2. 保险类的引用/依懒关系清晰,可注入
    3. 依懒于接口而非达成