目录

介绍

什么是TDD

TDD的好处

TDD不是什么

TDD生命周期

限制

什么是BDD?

TDD先决条件

例子

第1步:实体到DTO映射

第2步:Markdown到HTML转换

第3步:使用Markdown进行EnHance映射

第4步:设置数据库迁移

第5步:实体CRUD

第6步:测试服务

第7步:继续测试UI

编辑

视图

列表

结论


TDD和BDD用例子解释

介绍

在本文中,我将尝试解释什么是TDD以及它在开发过程中的帮助。有很多资源和书籍可以做到这一点,但我将尝试用一个简单的实例介绍。这比你在书中读到的严格定义更像是一个“哲学”概述。可能纯粹的这种方法的支持者会发现这个解释有点不完整(对不起......),但我认为这足以开始学习和理解基础知识。我的主要目的不是写另一本关于TDD的书,而只是用清晰简单的单词解释它是什么,这样初学者也可以理解和接受它。

完整的源代码可以在github上找到

什么是TDD

从维基百科的定义开始:

引用:

测试驱动开发TDD)是一个软件开发过程,它依赖于非常短的开发周期的重复:需求变成非常具体的测试用例,然后软件被改进以仅通过新的测试。这与软件开发相反,软件开发允许添加未经证明符合要求的软件。

清楚吗?TDD的主要目的是创建一种策略,其中测试将推动开发过程,以使编码更有效,更高效,减少回归的。

先决条件是以较小的步骤分解大任务并使用单元测试进行开发。这允许您处理较小的代码片段,使其工作,然后将许多工作部分集成在一起。

TDD的好处

将TDD引入您的编码体验将达到一个转折点。以下是最重要的好处的简短列表:

  1. 专注于非常重要的一点:你将被要求分解问题,这将有助于关注最重要的事情。
  2. 处理更简单的任务:每次使用单个、小型的任务可简化故障排除并加快开发速度。你将不会陷入编写所有代码的情况,然后某些东西不起作用,你不知道为什么。
  3. 简化的集成:当完成多个工作功能时,将所有功能放在一起将是一件愉快而轻松的任务。在回归的情况下,您将事先知道哪部分代码是坏的。
  4. 免费测试:完成完整任务后,许多单元测试仍然存在,可以用作集成\单元测试来验证代码并避免回归。

TDD不是什么

TDD是一种很好的方法,但不是:

  • 替代测试(单元测试,验收测试,UI测试)
  • 你可以在一天内学会的东西
  • 为你编写代码的东西
  • 一个神圣的人,可以驱除代码中的bug

TDD生命周期

TDD主要由三个步骤组成:

  1. 写单元测试(RED)。
  2. 让它工作(绿色)。
  3. 重构。

在该示例中,您可以编写单元测试,使用其中的代码实现该功能,直到它工作,然后重构将这段代码放在需要的地方。

步骤1,2:使测试工作

public class StripTest
{[Fact]public static void StripHTml(){string test="<h1>test</h1>";string expected="test";string result=StripHTML(test);Assert.Equal(expected,result);}public static string StripHTML(string input){return Regex.Replace(input, "<.*?>", String.Empty);}
}

3步:重构

public class StripTest
{[Fact]public static void StripHTml(){string test="<h1>test</h1>";string expected="test";string result=HtmlHelper.StripHTML(test);Assert.Equal(expected,result);}
}//somewhere else
public static class HtmlHelper
{public static string StripHTML(string input){return Regex.Replace(input, "<.*?>", String.Empty);}
}

限制

在许多情况下,很难编写涵盖实际代码使用情况的单元测试。完全逻辑的过程很容易,但是当我们要涉及数据库或UI时,编写工作的量会增加,并且在许多情况下,可能会超过好处。有一些最佳实践和框架对此有所帮助,但一般来说,并非应用程序的所有部分都可以使用普通单元测试进行测试。

什么是BDD?

BDD是TDD的增强,它考虑了单元测试是限制性的情况。此扩展使用开发人员作为单元测试,保持BDD的理念。您仍然可以将复杂任务分解为较小的任务,使用用户行为进行测试,和在纯后端任务中使用TDD具有相同的优势。

TDD先决条件

在团队合作中,除了了解所有涉及的技术之外,所有队友都必须了解并接受这一理念。

首先,您的代码必须由强大的单元测试系统授权:

  • .NET,.NET Core:内置Visual Studio或xunit(第二个是我个人的,首选的选择)
  • Java:junit运行得很好,我不需要找到另一个解决方案
  • PHP:PHP单元在所有情况下都适合我

然后,重要且必须:具有允许在测试期间模拟或重新创建正确行为的体系结构。我说的是在测试期间可以在内存或本地数据库中工作的ORM,也可以使用服务或存储库模式。使用DI框架(内置在.NET Core中,autofac或其他任何......)也有帮助。

最后但并非最不重要:一个完善的构建过程,集成在一个持续的集成流程中,除了正确的配置之外,确定哪个单元测试在集成期间在其上运行是有意义的,以及在本地运行的内容。

例子

让我们尝试在现实世界的例子中实践我们对TDD的了解。我想用这种方法创建一个wiki。我的意思是一个简单的维基,用户登录,写下标记页并发布。

首先,我会将“长”任务分解为较小的后续活动。每个子部分将使用小型单元测试开发。我将专注于wiki 页面CRUD。

第1步:实体到DTO映射

  1. 编写实体。
  2. 编写wiki 页面DTO。
  3. 编写将实体映射到DTO的代码。
// Database entitypublic class WikiPageEntity
{[DatabaseGenerated(DatabaseGeneratedOption.Identity)]public Guid Id { get; set; }public int Version { get; set; }public string Slug { get; set; }public string Body { get; set; }public string Title { get; set; }
}// DTO model in BLL
namespace WikiCore.Lib.DTO
{public  class WikiPageDTO{public string Title { get; set; }public string BodyMarkDown { get; set; }public string BodyHtml { get; set; }public int Version { get; set; }public string Slug { get; set; }}
}// From unit test, code omitted for brevity
public void EntityToDTO()
{WikiPageEntity source = new WikiPageEntity(){Title = "title",Slug = "titleslug",Version =1};var result = Mapper.Map<wikipagedto>(source);Assert.Equal("title", result.Title);Assert.Equal(1, result.Version);
}// From Mapping configuration, code omitted for brevitypublic MappingProfile()
{CreateMap<wikipageentity, wikipagedto="">().ReverseMap();
}

第2步:Markdown到HTML转换

  1. 创建一个转换markdown为HTML 的方法:
//Before refactoring public class MarkdownTest
{
[Fact]
public void ConvertMarkDown()
{var options = new MarkdownOptions{AutoHyperlink = true,AutoNewLines = true,LinkEmails = true,QuoteSingleLine = true,StrictBoldItalic = true};Markdown mark = new Markdown(options);var testo = mark.Transform("#testo");Assert.Equal("<h1>testo</h1>", testo);
}
// after refactoring ( method moved to helper
[Fact]
public void ConvertMarkDownHelper()
{Assert.Equal("<h1>testo</h1>", MarkdownHelper.ConvertToHtml("#testo"));
}// From markdown helper
public static class MarkdownHelper
{static MarkdownOptions options;static Markdown converter;static MarkdownHelper(){options = new MarkdownOptions{AutoHyperlink = true,AutoNewLines = true,LinkEmails = true,QuoteSingleLine = true,StrictBoldItalic = true};converter = new Markdown(options);}public static string ConvertToHtml(string input){Markdown mark = new Markdown(options);return mark.Transform(input);}
}    

第3步:使用Markdown进行EnHance映射

  1. 更改增加HTML字段计算的映射:
// mapped profile changed
public class MappingProfile : Profile
{public MappingProfile(){SlugHelper helper = new SlugHelper();CreateMap<wikipageentity, wikipagedto="">().ForMember(dest => dest.BodyMarkDown, (expr) => expr.MapFrom<string>(x => x.Body)).ForMember(dest => dest.BodyHtml, (expr) => expr.MapFrom<string>(x => MarkdownHelper.ConvertToHtml(x.Body))).ReverseMap();CreateMap<wikipagebo,wikipageentity>().ForMember(dest => dest.Body, (expr) => expr.MapFrom<string>(x => x.BodyMarkDown)).ForMember(dest => dest.Slug, (expr) => expr.MapFrom<string>(x => helper.GenerateSlug(x.Title)));}
}// From unit test, code omitted for brevity
public void EntityToDTO()
{WikiPageEntity source = new WikiPageEntity(){Body = "# prova h1",Title = "title",Slug = "titleslug",Version =1};var result = Mapper.Map<wikipagedto>(source);Assert.Equal("title", result.Title);Assert.Equal(1, result.Version);Assert.Equal("<h1>prova h1</h1>", result.BodyHtml);
}

第4步:设置数据库迁移

  1. 运行Add-Migration脚本。
  2. 创建一个在内存中工作的单元测试来测试它。
[Fact]
public void MigrateInMemory()
{var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();optionsBuilder.UseInMemoryDatabase();using (var db = new DatabaseContext(optionsBuilder.Options)){db.Database.Migrate();}// No error assert migration was OK
}

第5步:实体CRUD

  1. 写一个CRUD测试。
  2. 测试一下。
[Fact]
public void CrudInMemory()
{var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();optionsBuilder.UseInMemoryDatabase();using (var db = new DatabaseContext(optionsBuilder.Options)){db.Database.Migrate(); db.WikiPages.Add(new Lib.DAL.Model.WikiPageEntity(){Title = "title",Body = "#h1",Slug = "slug"});db.SaveChanges();var count=db.WikiPages.Where(x => x.Slug == "slug").Count();Assert.Equal(1, count);// update, delete steps omitted for brevity}
}

第6步:测试服务

  1. 使用业务逻辑创建服务。
  2. 测试一下。
[Fact]
public void TestSave()
{var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();optionsBuilder.UseInMemoryDatabase();using (var db = new DatabaseContext(optionsBuilder.Options)){db.Database.Migrate();db.SaveChanges();//this recreate same behaviour of asp.net MVC usageDatabaseWikiPageService service = new DatabaseWikiPageService(db, Mapper.Instance);service.Save(new Lib.BLL.BO.WikiPageBO(){BodyMarkDown="#h1",Title="prova prova"});var item = service.GetPage("prova-prova");Assert.NotNull(item);}
}

第7步:继续测试UI

一旦使用单元测试测试UI变得复杂,我就切换到BDD并完成了多个步骤来完成UI测试。因此,我不是编写所有代码然后测试它,而是在多个子活动中分解问题并逐个测试:

编辑

  1. 准备表单,并进行测试。
  2. 准备模型,测试从表单提交的内容填充后端模型。
  3. 集成服务以保存数据,进行测试。

视图

  1. 准备模型,传递到视图,测试它。
  2. 将模型与服务集成,以获得真实数据。测试一下。

列表

  1. 准备视图模型,将假数据传递给UI,测试它。
  2. 整合服务,测试它。

结论

TDD是一种推动测试支持的开发过程的方法。这有助于以多种方式编码,但要求所有队友都有一些基础知识。一旦完成此步骤,您将处理更简单的任务和许多可以重用的测试。如果开发时需要编写单元测试,这个过程将有助于避免回归并更快地达到目标。此外,如果您的应用程序由于复杂性而难以测试,那么您可以保持执行BDD的相同理念。

原文地址:https://www.codeproject.com/Articles/1267361/Build-an-ASP-NET-Wiki-to-Explain-TDD

构建一个ASP.NET Wiki来解释TDD相关推荐

  1. 如何一秒钟从头构建一个 ASP.NET Core 中间件

    前言 其实地上本没有路,走的人多了,也便成了路. -- 鲁迅 就像上面鲁迅说的那样,其实在我们开发中间件的过程中,微软并没有制定一些策略或者文档来约束你如何编写一个中间件程序, 但是其中却存在者一些最 ...

  2. 从零开始构建一个的asp.net Core 项目(一)

    最近突发奇想,想从零开始构建一个Core的MVC项目,于是开始了构建过程. 首先我们添加一个空的CORE下的MVC项目,创建完成之后我们运行一下(Ctrl +F5).我们会在页面上看到"He ...

  3. 从零构建一个简单的 Python 框架

    为什么你想要自己构建一个 web 框架呢?我想,原因有以下几点: 你有一个新奇的想法,觉得将会取代其他的框架 你想要获得一些名气 你遇到的问题很独特,以至于现有的框架不太合适 你对 web 框架是如何 ...

  4. python框架实例,从零构建一个简单的 Python 框架

    为什么你想要自己构建一个 web 框架呢?我想,原因有以下几点: 你有一个新奇的想法,觉得将会取代其他的框架 你想要获得一些名气 你遇到的问题很独特,以至于现有的框架不太合适 你对 web 框架是如何 ...

  5. AI:2023年6月9日北京智源大会演讲分享之基础模型前沿技术论坛—《工程化打造AI中的CPU》、《构建一个AI系统:在LLM上应用带有RLHF来推进定制》、《多模态预训练的进展回顾与展望》、《扩展大

    AI:2023年6月9日北京智源大会演讲分享之基础模型前沿技术论坛-<工程化打造AI中的CPU>.<构建一个AI系统:在LLM上应用带有RLHF来推进定制>.<多模态预训 ...

  6. php mysql弹幕_如何使用PHP构建一个高性能的弹幕后端服务

    如何使用PHP构建一个高性能的弹幕后端服务 随着WEB2.0的流行,现在很多网站都流行使用"弹幕"这种形式来实现互动. 弹幕(barrage),中文流行词语,原意指用大量或少量火炮 ...

  7. python构建知识图谱_NLP第20课:Neo4j 从入门到构建一个简单知识图谱

    Neo4j 对于大多数人来说,可能是比较陌生的.其实,Neo4j 是一个图形数据库,就像传统的关系数据库中的 Oracel 和 MySQL一样,用来持久化数据.Neo4j 是最近几年发展起来的新技术, ...

  8. Neo4j 从入门到构建一个简单知识图谱

    Neo4j 对于大多数人来说,可能是比较陌生的.其实,Neo4j 是一个图形数据库,就像传统的关系数据库中的 Oracel 和 MySQL一样,用来持久化数据.Neo4j 是最近几年发展起来的新技术, ...

  9. 如何用 Slack 和 Kubernetes 构建一个聊天机器人?| 附代码

    作者 | Alexander Kainz 译者 | 天道酬勤,责编 | Carol 出品 | AI科技大本营(ID:rgznai100) ChatOps可以让你使用基于聊天的接口来管理DevOps任务 ...

最新文章

  1. 配置JDKAndroid 2D游戏引擎AndEngine
  2. 号称用GPT-3开发的游戏,却活生生玩出了人性黑暗面
  3. php爬取ckplayer,CKplayer站外调用示例附最终效果
  4. Eclipse下Tomcat插件的安装
  5. HDU 3068 最长回文
  6. jpa添加索引的几种方式
  7. 可怕!那些你看不到的进程
  8. 必须学会使用的35个Jquery小技巧
  9. ajax修改按钮的html值,表格行的按钮AJAX后,怎么修改表格当前行的值
  10. nginx tcp转发_Nginx学习(九):负载均衡服务
  11. ESP32 coredump 分析
  12. 数学菜鸟和大佬的关注点有什么不同?
  13. 信息学奥赛一本通 1345:【例4-6】香甜的黄油 | 洛谷 P1828 [USACO3.2]香甜的黄油 Sweet Butter
  14. 浅谈!important对CSS的重要性
  15. 3D图像生成和编辑研究成果大放送!朱俊彦团队放出两篇论文实现代码 | 资源...
  16. 使用Julia进行图像处理--使用形态学运算进行图像调整
  17. 显示浏览器表单输入框的缓存密码
  18. 通过自定义注解+反射的形式,使用POI实现excel的导入导出
  19. python爬豆瓣top250书籍_python爬虫练习-爬取豆瓣图书top250
  20. 递归算法详解——递归算法的三要素以及例题分析

热门文章

  1. 锤子t1重置后怎么显示无服务器,解决锤子手机smartisanT1关机后无法正常开机(附带刷机教程图文)...
  2. UI设计灵感|3D\C4D元素网站,流行最前沿
  3. 极简风海报作品合集|过目不忘的海报大片,越看越过瘾
  4. 久其修改服务器地址,久其修改服务器地址
  5. 网易云音乐刷听歌量_网易云音乐极速版悄然上线!听歌体验同之前没有差别
  6. C++四种强制类型转换解析
  7. 导入要素到要素类(C++)ArcEngine开发
  8. Linux BPF hello world C语言示例代码
  9. Java native方法String转char*以及String[]转char**
  10. wpf label字体为斜体_2020年趋势:20款流行字体大预测