构建一个ASP.NET Wiki来解释TDD

目录

介绍

什么是TDD

TDD的好处

TDD不是什么

TDD生命周期

限制

什么是BDD?

TDD先决条件

例子

第1步:实体到DTO映射

第2步:Markdown到HTML转换

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

第4步:设置数据库迁移

第5步:实体CRUD

第6步:测试服务

第7步:继续测试UI

编辑

视图

列表

结论


TDDBDD用例子解释

介绍

在本文中,我将尝试解释什么是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

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

TDD先决条件

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

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

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

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

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

例子

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

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

1步:实体到DTO映射

  1. 编写实体。
  2. 编写wiki 页面DTO
  3. 编写将实体映射到DTO的代码。
// Database entity
 public 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 brevity
 public MappingProfile()
{
    CreateMap<wikipageentity, wikipagedto="">().ReverseMap();
}

2步:MarkdownHTML转换

  1. 创建一个转换markdownHTML 的方法:
//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 usage
        DatabaseWikiPageService 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值