• 如何对遗留代码进行单元测试(scrumgathering听后感)


    上周末去听了Scrumgathering的试讲活动,感觉此类活动还是挺有意思的,一群scrum实践者或者爱好敏捷的同学在一起讨论如何做好敏捷项目,这次主要听了一场关于如何对遗留代码做单元测试的演讲,因此向记录一下一些很好的观点,来用于我们对单元测试的理解,以及如何提高代码可测性。

    所谓的遗留代码(legacy code),简单就是指没有任何测试的代码。那么我们如何来对这些遗留代码进行测试,或者是通过修改使这些遗留代码能够变的更加testability。

    案例一、

    public class Car{

    private Engine engine;

    public Car(){

    engine = new Engine(10);

    }

    public boolean isMove(){

      return engine.speed()>0? true:false;

    }

    }

    针对这个class的test case应该是这样的:

    public class TestCase{

    private Car car;

    public void testMove(){

     car = new Car();

    Assert.assertEquals(true,car.isMove());

    }

    }

    对于这个测试用列,我们并没有真正的达到测试Car的isMove方法的正确性,因为Car在这里对Engine类的一个依赖,在这个测试用列中我们没法达到测试isMove方法中的逻辑。因此我们需要对Car这段遗留代码进行解依赖。

    对Car这个类的构造函数进行修改

    public class Car{

    private Engine engine;

    public Car(Engine engine){

    this.engine = engine;

    }

    public boolean isMove(){

    return engine.speed()>0?true:false;

    }

    }

    测试代码如下:

    public class TestCase{

    public Car car;

    public void testIsMove(){

    Engine engine = new MockEngine(10);

    car = new Car(engine);

    Assert.assertEquals(true,car.isMove());

    }

    }

    可以看到这时的测试代码已经完完全全的在测试isMove方法了,我们不需要关心Engine,我们可以随便mock一个engine对象。这样就完成了对Engine的解依赖,使代码具有可测性。

    案例二、

    提高方法的可见性,如果实际工作中我们需要对一个private的方法进行测试的话,我们可能会无从下手,对于这类测试,我们可以说是该方法无法在测试工具中导入,那么这个时候,我们需要提升一下方法的权限,比如我们可以将private方法改为protected,那么我们就可以通过子类继承被测类,子类中对protected方法是可见的,那么我们就可以将子类导入到测试用具中完成测试工作。

    案例三、

    虽然目前已经有很多的mock工具,如jmock, easymock等工具,这些工具可以很方便的构造mock类,但是这些类的可读性会比较差,因此提倡自己写mock类,这样不但可以增加可读性,并且对于mock类的实现我们可以比较自由。

    案例四、

    对于较大类的解决,在实际工作中我们可能会遇到一个非常庞大的类,这个类可能有几百个方法,那么如何提高这种类的可测性,提倡一个原则(单一职责原则SRP),每个类应该仅承担一个职责:它在系统中的意图应当是单一的,且修改它的原因应该只有一个。

    案例五、

    如果需要在遗留代码中添加一些新的功能,一些非常简单的功能的时候我们应该怎么做,如下代码,我们需要在parse方法之后加上日志记录,那么我们可以通过以下几种方法来做:

    public class Parser{

    public void parse(){

      .........

    }

    }

    我们可以添加一个方法叫做parseWithLogger().

    如:

    public class Parser{

     public void parseWithLogger(){

        Logger loger = Logger.log(.....);

        parse();

    }

     public void parse(){

     ......

    }

    }

    这样我们不需更改原来的parse方法进行修改,我们之需要增加一个新的方法,然后在我们测试用具中对这个新的方法进行测试就行。

    我们也可以这样更改代码,把原方法改名为parseWithoutLogger().

    public class Parser{

     public void parseWithoutLogger(){

       ...........

    }

    public void parse(){

      Logger log = Logger.log();

      parseWithoutLogger();

    }

    }

    这样对代码的改动也不是很大,我们可以在测试用具中测试parse方法是否加logger。

    两种方法对遗留代码都不会造成大改变,我们强调对遗留代码的修改一定要小心,因为遗留代码是没有测试代码,我们不能大刀阔斧的进行修改,只能小步前进,然后对遗留代码加上测试用列,保证重构的正确性。

    当然对遗留代码的可测性提高还有很多方法,可以参考Michale Feathers 写的<working effectively with legacy code>.同时也非常感谢@姚若舟 周末分享的How to write unit test for new code based on legacy code。 非常精彩的演讲。

  • 相关阅读:
    Java实验四
    Java第八周学习总结
    Java实验三
    输入输出练习
    第七周
    20145113 实验二 Java面向对象程序设计
    2020-2021-1 20209306 《linux内核原理与分析》第九周作业
    2020-2021-1 20209306 《linux内核原理与分析》第八周作业
    2020-2021-1 20209306 《linux内核原理与分析》第七周作业
    2020-2021-1 20209306 《linux内核原理与分析》第六周作业
  • 原文地址:https://www.cnblogs.com/victorcai0922/p/2512410.html
Copyright © 2020-2023  润新知