• 大话重构连载14:我们是这样自动化测试的


    说了那么多,让我们用示例看看,系统重构是应该怎样做自动化测试的。还是回到前面那个HelloWorld的例子(详见 3.3 小步快跑是这样玩的),该类中有一个sayHello()方法,只要我们输入当前的时间与用户名,就返回对该用户的问候语。如果当前时间是上午,则返回“Hi, XXX. Good morning!”;如果是下午,则返回“Hi, XXX. Good afternoon!”;如果是晚上,则返回“Hi, XXX. Good Night!”,这是HelloWorld这个程序实现的功能。

    然后我们开始为这段程序编写测试代码(如果采用测试驱动开发,应当先写测试代码再写程序)。我们首先建立一个test源程序目录,然后建立与被测程序对应的包和测试程序。这就是说,如果被测程序在“org.refactoring.helloWorld.resource”包中,则测试程序应当建立“test.org.refactoring.helloWorld.resource”包与之对应;如果被测程序叫“HelloWorld”,则建立“HelloWorldTest”类与之对应,这个类是一个JUnit测试程序。

    下面就是编写这个测试程序执行测试了。由于被测程序有三个分支,即当前时间是上午、下午、晚上,因此我们分别为之建立了三个测试用例,测试程序如下:

     1 /**
     2  * Test for {@link org.refactoring.helloWorld.resource.HelloWorld}
     3  * @author fangang
     4  */
     5 public class HelloWorldTest {
     6 
     7     private HelloWorld helloWorld = null;
     8     /**
     9      * @throws java.lang.Exception
    10      */
    11     @Before
    12     public void setUp() throws Exception {
    13         helloWorld = new HelloWorld();
    14     }
    15 
    16     /**
    17      * @throws java.lang.Exception
    18      */
    19     @After
    20     public void tearDown() throws Exception {
    21         helloWorld = null;
    22     }
    23 
    24     /**
    25      * Test method for {@link org...HelloWorld#sayHello(java.util.Date, java.lang.String)}.
    26      */
    27     @Test
    28     public void testSayHelloInTheMorning() {
    29         Date now = DateUtil.createDate(2013, 9, 7, 9, 23, 11);
    30         String user = "鲍晓妹";
    31         String result = "";
    32         result = helloWorld.sayHello(now, user);
    33         assertThat(result, is("Hi, 鲍晓妹. Good morning!"));
    34     }
    35 
    36     /**
    37      * Test method for {@link org...HelloWorld#sayHello(java.util.Date, java.lang.String)}.
    38      */
    39     @Test
    40     public void testSayHelloInTheAfternoon() {
    41         Date now = DateUtil.createDate(2013, 9, 7, 15, 7, 10);
    42         String user = "关二锅";
    43         String result = "";
    44         result = helloWorld.sayHello(now, user);
    45         assertThat(result, is("Hi, 关二锅. Good afternoon!"));
    46     }
    47 
    48     /**
    49      * Test method for {@link org...HelloWorld#sayHello(java.util.Date, java.lang.String)}.
    50      */
    51     @Test
    52     public void testSayHelloAtNight() {
    53         Date now = DateUtil.createDate(2013, 9, 7, 21, 30, 10);
    54         String user = "IT攻城狮";
    55         String result = "";
    56         result = helloWorld.sayHello(now, user);
    57         assertThat(result, is("Hi, IT攻城狮. Good night!"));
    58     }
    59 }

    这段程序采用的是JUnit4编写的,其中assertThat(result, is("Hi, IT攻城狮. Good night!"));,第一个参数是被测程序执行的结果,而第二个参数是根据期望结果进行验证。如果执行结果与预期结果相同,则测试通过,否则测试失败。

    随后我们运行该测试程序,得到如下结果:

     

    图4.1 JUnit测试结果

     

    三项测试用例全部通过,测试成功!

    现在我们为原程序编写了测试用例并全部测试通过,我们为重构所做的准备工作就一切就绪了。然后,我们开始进行第一次重构。如前面所述,第一次重构我们调整了程序的顺序,进行了分段,增加了注释,并修改了相应的变量,使其更加利于阅读。这是一个小步快跑的过程,我们完成此次重构只花费了3、5分钟。当重构完成,程序重新回到可编译运行状态时,我们执行它的这个测试程序,测试通过。测试通过意味着,虽然程序内部的代码有所修改,但程序对外的功能没有变化,即程序的外部行为没有变化,则重构成功,我们可以继续后面的工作。

    第二次重构,我们运用“抽取方法”,从sayHello()函数中抽取出了getFirstGreeting(), getSecondGreeting(), getHour()三个方法。之后我们再次执行测试程序,测试通过。

    第三次重构,我们运用“抽取类”,将getFirstGreeting()与getSecondGreeting()分别抽取出来形成了GreetingToUser和GreetingAboutTime。完成之后执行测试通过。

    第四次重构,我们的需求发生了变化,问候语不仅随一天中的上午、下午、晚上等进行变化,还需要根据不同的日期判断是否是节日。在这种情况下,我们采用“两顶帽子”的方式进行开发:首先不引入新的需求,仅仅修改原程序,使之适应新需求。为此我们从GreetingAboutTime类中提炼出DateUtil,使之不仅有getHour(),还有getMonth()与getDate()。完成重构以后测试通过。

    关于“两顶帽子”的设计方式,也是系统重构中另一个不同以往的地方,我们还将在后面详细地进行讨论。随后我们开始添加新需求,使GreetingAboutTime中的getGreeting()写成这样:

     1 /**
     2  * Test for {@link org.refactoring.helloWorld.resource.HelloWorld}
     3  * @author fangang
     4  */
     5 public class HelloWorldTest {
     6 
     7     private HelloWorld helloWorld = null;
     8     /**
     9      * @throws java.lang.Exception
    10      */
    11     @Before
    12     public void setUp() throws Exception {
    13         helloWorld = new HelloWorld();
    14     }
    15 
    16     /**
    17      * @throws java.lang.Exception
    18      */
    19     @After
    20     public void tearDown() throws Exception {
    21         helloWorld = null;
    22     }
    23 
    24     /**
    25      * Test method for {@link org...HelloWorld#sayHello(java.util.Date, java.lang.String)}.
    26      */
    27     @Test
    28     public void testSayHelloInTheMorning() {
    29         Date now = DateUtil.createDate(2013, 9, 7, 9, 23, 11);
    30         String user = "鲍晓妹";
    31         String result = "";
    32         result = helloWorld.sayHello(now, user);
    33         assertThat(result, is("Hi, 鲍晓妹. Good morning!"));
    34     }
    35 
    36     /**
    37      * Test method for {@link org...HelloWorld#sayHello(java.util.Date, java.lang.String)}.
    38      */
    39     @Test
    40     public void testSayHelloInTheAfternoon() {
    41         Date now = DateUtil.createDate(2013, 9, 7, 15, 7, 10);
    42         String user = "关二锅";
    43         String result = "";
    44         result = helloWorld.sayHello(now, user);
    45         assertThat(result, is("Hi, 关二锅. Good afternoon!"));
    46     }
    47 
    48     /**
    49      * Test method for {@link org...HelloWorld#sayHello(java.util.Date, java.lang.String)}.
    50      */
    51     @Test
    52     public void testSayHelloAtNight() {
    53         Date now = DateUtil.createDate(2013, 9, 7, 21, 30, 10);
    54         String user = "IT攻城狮";
    55         String result = "";
    56         result = helloWorld.sayHello(now, user);
    57         assertThat(result, is("Hi, IT攻城狮. Good night!"));
    58     }
    59 }

    之后我们的测试不能通过:

     

    图4.2 测试用例不能通过

     

    为什么testSayHelloAtNight测试不能通过呢?仔细查看被测程序,我们发现它的功能发生了变化,变为:如果当前时间是1月1日,则返回“Hi, XXX. Happy new year!”;如果是1月14日,则返回“Hi, XXX. Happy valentine's day!”……如果当前时间都不是这些节日,如果是上午则返回“Hi, XXX. Good morning!”,是中午则返回“Hi, XXX. Good noon!”,是下午则返回“Hi, XXX. Good afternoon!”,是傍晚则返回“Hi, XXX. Good evening!”,否则才返回“Hi, XXX. Good night!”。正因为如此,我们需要调整我们的测试程序,为每一个分支编写测试用例。测试修改好后,最后测试通过。

    大话重构连载首页:http://www.cnblogs.com/mooodo/p/talkAboutRefactoringHome.html

    特别说明:希望网友们在转载本文时,应当注明作者或出处,以示对作者的尊重,谢谢!

  • 相关阅读:
    [LeetCode179]Largest Number
    [LeetCode27]Remove Element
    [LeetCode238]Product of Array Except Self
    [LeetCode169]Majority Element求一个数组中出现次数大于n/2的数
    [LeetCode202]Happy Number判断一个数是不是happy number
    [LeetCode283]Move Zeros将一个数组中为0的元素移至数组末尾
    [LeetCode136]Single Number寻找一个数组里只出现一次的数
    iOS 9: UIStackView入门
    优化UITableViewCell高度计算的那些事
    AutoLayout深入浅出五[UITableView动态高度]
  • 原文地址:https://www.cnblogs.com/mooodo/p/WeDoAutomationTestLikeThis.html
Copyright © 2020-2023  润新知