zoukankan      html  css  js  c++  java
  • 终结DbHelper鬼画符2 Tdd全攻略

        在这一篇中,我们将采用Step By Step的方式,重点介绍如何在实际开发过程中,使用测试先行、代码复查和重构。形成这样的开发习惯之后,编写代码将不再是实现、排错的过程,而变成制造快乐的过程:写一个测试、实现、测试通过,再写测试、再实现、再通过测试。完成一件任务,能引发多处阶段性的快乐,这对于我们的工作心态大有好处。

        我们的工作目标,是为Db类增加三个构造方法。

        先解释一下为什么要先写测试,再写代码,实际上,有下面的七个方面的好处:

         1、关注点集中

       2、单一的成功被无数成功取代,心态将更为良好:脑力工作,情绪是非常重要的,大家如果正常的统计自己的有效工作时间,往往会发现自己一天的有效工作状态持续时间非常短。一个很经典的说法,是多数程序员每天正常工作的时间平均下来不超过一个小时。

        3、占在类的用户角度,来看待类的设计,有助于

        4、建立起整个项目的质量基础

        5、累积下来的单元测试,是后期代码修改的准则

        6、Bug的修复将更为精准。

        7、单元测试,就是团队中其他程序员的使用手册。

       一鱼七吃,这样的事情,为什么不做?

        代码复查,简单的说就是逐行阅读代码,从而找出潜在的问题和代码结构上的坏味道。据说,代码复查能够发现项目中一半以上的Bug,当然,在代码尚未发布、或者Bug尚未转移到团队其他成员处的时候,修复Bug的成本是最低的。

        代码复查是很枯燥的工作,自己复查、团队成员交互复查都行。敏捷方法中比较极端的例子是双人编程,这样,实际上代码复查贯穿于整个开发时间。

        重构,消除代码中坏的味道,这里最常见的一种现象是“重复的代码”。

        由于有大量的单元测试,重构过程中保持全部单元测试通过,有助于实现我们的目标:仅仅调整代码的结构,而不会令功能实现上出现问题。

        代码质量,并不是代码工作如何精准,而是代码的结构是否做到了最简化。请注意,稍稍不留意,代码增加的少许复杂性,会令同事阅读困难、甚至令自己阅读困难,各处的复杂性累积之后,会造成Bug数量的大幅增长,维护也变得困难。这里可以参考用户体验的一句名言“多一次击键,用户可能永远不会使用这项功能”。

        唠叨完毕,我们先做好准备。

        我们创建一个单元测试项目,引用我们的工作项目Faster.Data类库项目。在单元测试项目中增加一个配置文件App.config,我们将数据库连接信息保存在这个配置文件中。

        单元测试中最常见的问题,是“数据库如何测试?”

       我曾经饶有兴趣的使用Mock,但最终对自己定下了规矩,即今后绝不使用任何Mock。原因:Mock对象会大幅增加单元测试的简洁性,构建Mock对象有时候也需要大量的工作,充满了Mock的单元测试可读性很差,团队成员需要全体掌握相关的知识从而导致对开发人员的门槛提高,这也意味着成本的提高。

        不过,在这个类库创建完毕后,数据库读写操作就有了很好的基础,今后所有应用项目则根本无需对数据库读写进行单元测试。

         我的方法是,建立测试数据库,保证数据库所有表格处于空白的无记录状态,即针对每一项测试,测试前要准备数据,测试后毁尸灭迹。

        那么,现在开始为Db增加构造方法,我们先做最原始的:

        第一步 在类图中为Db添加一个构造方法 Db(string connectionString,string providerName)

        为什么先做这个?因为这是与配置文件无关的,Db(string configName),这个从配置文件中获取上述两个参数,Db()则默认的使用ApplicationServices配置项获取上述两个参数。我们显然要先做最简单的。换句话说:先解决构造问题,再解决配置文件的问题。

        第二步,切换到这个构造方法的代码,右键,选择创建单元测试。此时,Ide帮我们在下是项目中增加了一个代码文件,测试类的名称为DbTest,嗯,类名加上Test。

        那么,这是第一个基本的概念,测试是以类为单位的,每一个待测试的类,对应一个测试类。多数情况下,类的每一个public成员,对应一个测试方法。我们要牢记两点:1、不针对私有成员写测试;2、每一个测试方法都要足够的简单,突出该方法的目的。

         第三步,我们在测试方法中,写上这样的代码

         Db db= new ("连接字符串","提供者名称");

         Assert.IsNotNull(db);  //我们期望上一行创建的db对象不为空

         第四步,运行测试,出现绿色的“成功”提示。好,现在这个测试已经通过。

         第五步,现在我们要确定Db已经正常的连接到数据库。对,如您所说,Db的State状态必须为Open

         我们在测试方法下,再增加一行

         Assert.AreEqual(ConnectionState.Open,Db.State);//期望创建的连接已经打开

         运行测试,当然失败了,因为我们还没有做真正的连接工作。

        第六步,我们为刚刚写的Db的构造方法中增加如下的代码:

    factory = DbProviderFactories.GetFactory(provider);           
    connection = factory.CreateConnection();
    connection.ConnectionString =connectionString;
    connection.Open();

        第七步,运行测试,通过。嗯,现在这项工作完成。

        按照同样的方式,创建其他两个构造方法。比如:

        第八步:从某个配置项中定义的连接字符串,创建连接

    var setting = ConfigurationManager.ConnectionStrings[config];

    factory = DbProviderFactories.GetFactory(provider);           
    connection = factory.CreateConnection();
    connection.ConnectionString =connectionString;
    connection.Open();

        第九步 重构

        我们简单的审阅代码,会发现第六步和第八步的代码,显然有多数重复。那么,创建一个私有方法CreateConnection,包含如下代码:

    factory = DbProviderFactories.GetFactory(provider);           
    connection = factory.CreateConnection();
    connection.ConnectionString =connectionString;

    然后,第六步的代码便是:

    CreateConnection(connectionString, provider);

    第八步的代码变为:

    var setting = ConfigurationManager.ConnectionStrings[config];
    CreateConnection(setting.ConnectionString, setting.ProviderName);

    重复,是最常见的“坏味道”,这里是最浅显的例子。

    然后,发现这种从抽象类DbConnection继承,然后内置一个DbConnection对象,将所有操作转移到该对象的方法,实现起来颇多不自然之处。包括:

    1、我们实现了所有的抽象成员,但GetScheme之类的成员没有实现。

    2、内置的protected只读属性DbProviderFactory没有实现

    3、这个对象实际上包括两个DbConnection的实例,一个是它自身,一个是内置的connection实例,思路上比较紊乱。

    此时面临着问题:是采用静态类的方法,还是这种创建自定义Connection的方法,于是我们在团队项目中增加一个问题Impediments

    在项目开发过程中往往面临着各种问题,有的是设计上的决策,有的是技术难题。每个成员当遇到这类影响进度的问题时,需要提交相应的信息,至迟在第二天早上的15分钟例会中,项目经理和团队其他成员应能够注意到,并优先解决这些问题。问题的优先级大于任务。

    比如,这里如果要更改设计,则面临着前面的单元测试要重写、这个类的所有代码要重新组织。

    此时,我们最终选择改用静态类的方式去做。这样,我们关闭这个问题,因为我们已经做出了决策。

    增加一个任务:重构,Db改用静态类方式,不再从DbConnection继承。

    经过半小时的工作,这个任务完成,新写的4个单元测试正常运行通过。由此,我们知道,由于有单元测试基础,这种更改设计的决策,比较容易下决心。

    如此,我们通过创建4个单元测试、从而创建三个构造方法,完成了这一组任务。过程中,根据进度,在Tfs团队项目中将各项task的状态分别由in progress 改为done。

    看看,比较满意吧?运行所有测试,通过。相关的任务,done。将代码签入到源代码服务器,接着做剩下的其他工作…

  • 相关阅读:
    Java实现 洛谷 P1060 开心的金明
    (Java实现) 洛谷 P1605 迷宫
    (Java实现) 洛谷 P1605 迷宫
    (Java实现)洛谷 P1093 奖学金
    (Java实现)洛谷 P1093 奖学金
    Java实现 洛谷 P1064 金明的预算方案
    Java实现 洛谷 P1064 金明的预算方案
    (Java实现) 洛谷 P1031 均分纸牌
    QT树莓派交叉编译环开发环境搭建(附多个exe工具下载链接)
    武则天红人对唐睿宗的桃色报复(如此缺少城府,注定了要在宫廷中过早地出局)
  • 原文地址:https://www.cnblogs.com/by1990/p/1973425.html
Copyright © 2011-2022 走看看