软件测试技术之有效的单元测试
凌雪 2018-10-25 来源 :网络 阅读 1078 评论 0

摘要:本文将带你了解软件测试技术之有效的单元测试,希望对大家学测试技术有所帮助。

本文将带你了解软件测试技术之有效的单元测试,希望对大家学测试技术有所帮助。


前言
        对之前的项目进行重构,由于之前的项目中的单元测试大部分都是走走形式,对单元测试疏于管理,运行之后大部分是不通过,这样的单元对项目而言毫无价值,更不要说有助于理解系统功能。这也使我有契机了解到TDD(测试驱动开发)的思想。为了在项目重构中编写有效的单元测试,我查找了有关TDD的一些书籍,《单元测试的艺术》(Roy   Osherove著)和《有效的单元测试》(科斯凯拉著)都是有关测试驱动开发的不错的书籍,前者是使用.net语言,后者使用java语言,作为java程序员我自然选择了后者。但实际上作者在阐述一种思想,不论哪种语言都可以读懂,只是平时的习惯,对于熟悉的语言读起来更顺畅。这篇文章也是对书中的内容做一个总结。
      一、单元测试代码的可读性
      ①使用更易懂的API,把你的代码读出来
      示例:
      //代码一
      String msg = “hello,World”;
      assertTrue(msg.indexOf(“World”)!=-1);
      //代码二
      String msg = “hello,World”;
      assertThat(msg.contains(“World”),equals(true));
      同样断言字符串中包含 World 这个单词,代码一中 使用indexOf   这个取得单词索引位置的API就显得间接许多,而且我们的大脑还需要对表达式进行判断,进一步增加了认知的负担,而contains   方法字面意思就是包含,更符合我们要表达的意思。所以一定要找到更适合易懂的API。同时用assertThat方法替代assertTrue方法,使的整个语句更具口语化,完全可以像读文章一样读出来
      ②避免使用较底层的方式,比如位运算符(这并不能表示你有多牛 =.=)
      示例:
      //代码一
      public class PlatformTest {
      @Test
      public void platformBitLength(){
      assertTrue(Platform.IS_32_BIT ^ Platform.IS_64_BIT);
      }
      }
      //代码二
      public class PlatformTest {
      @Test
      public void platformBitLength() {
      assertTrue("Not 32 or 64-bit platform?", Platform.IS_32_BIT ||   Platform.IS_32_BIT);
      assertFalse("can't be 32 and 64-bit at the same   time.",Platform.IS_32_BIT && Platform.IS_32_BIT);
      }
      }
   
      代码一 要检查的是什么?位运算符结果怎么算?恐怕大部分使用高级语言的程序员很少会用到,这会增加我们的认知负担。
      位运算符可能会有效的执行一个程序,但单元测试的代码可读性优于性能,我们应该更好的表达我们的意图,使用布尔运算符来替换位运算符可以更好的表达意图,见示例二。
      ③不要在测试中对代码进行过度运用防御性策略
      1 public void count(){
      2         Data data =   project.getData();
      3         assertNotNull(data);
      4           assertEquals(4,data.count());
      5 }
        第三行代码有些画蛇添足,即使data为空,在没有第三行代码的情况下,测试案例依然会失败,在IDE中双击失败信息,可以快速跳转到失败行,并指出失败原因。所以第三行代码并没有意义,这种防御性策略的真正优势在于方法链中抛出空指针的时候。比如   assertEquals(4,data.getSummary().getTotal()),当此行代码抛出空指针异常时,你无法判断是data为空还是data.getSummary()为空,此时可以先进行assertNotNull(data)的断言。
      二、单元测试代码的可维护性
      ①去除重复,包括结构性重复
      1 //代码一
      2 public class TemplateTest(){
      3      @Test
      4     public void emptyTemplate()   throws Exception{
      5         String template=“”;
      6         assertEquals(template,new   Template(template).getType());
      7    }
      8     @Test
      9     public void plainTemplate()   throws Exception{
      10         String   template=“plaintext”;
      11         assertEquals(template,new   Template(template).getType());
      12   }
      13 }
      两个测试方法,一个是测试建立一个空模板,另一个测试建立一个纯文本模板,明显可以发现存在结构性重复,对以上代码进行改进,如下:
      1 //代码二
      2 public class TemplateTest(){
      3      @Test
      4     public void emptyTemplate()   throws Exception{
      5           assertTemplateType(“”);
      6     }
      7     @Test
      8     public void plainTemplate()   throws Exception{
      9           assertTemplateType(“plaintext”);
      10     }
      11    private void   assertTemplateType(String template){
      12         assertEquals(template,newTemplate(template).getType())
      13    }
      14 }
      虽然代码行数没有减少,甚至还多了一行,但是把相同的代码提炼到一处,当它发生变动时只需修改一处,可维护性增强了。
      ②避免由于条件逻辑而造成的测试遗漏,存在条件逻辑时要在最后加上 fail()方法,强制测试失败
      考虑一下,当Iterator 为空的时候,下面的测试方法会失败吗?
      1 //重构前
      2 public class DictionaryTest{
      3 @Test
      4 public void testDictionary() throws Exception{
      5     Dictionary dict = new Dictionary();
      6     dict.add(“A”,new   Long(3));
      7     dict.add(“B”,”21”);
      8     for(Iterator e =   dict.iterator();e.hasNext()){
      9         Map.Entry entry =   (Map.Entry) e.next();
      10           if(“A”.equals(entry.getKey()))
      11               asserEquals(3L,entry.getValue());
      12           if(“B”.equals(entry.getKey()))
      13               assertEquals(“21”),entry.getValue();
      14      }
      15   }
      16 }
      显然当Iterator为空时,测试并不会失败,这并不符合我们单元测试的目的,进行重构后:
      1 //重构后
      2 public class DictionaryTest{
      3 @Test
      4 public void testDictionary() throws Exception{
      5     Dictionary dict = new   Dictionary();
      6     dict.add(“A”,new   Long(3));
      7     dict.add(“B”,”21”);
      8       assertContain(dict.iterator(),”A”,3L);
      9           assertContain(dict.iterator(),”B”,21);
      10   }
      11 private void assertContain(Iterator i,Object key,Object value){
      12           while(i.hasNext()){
      13             Map.Entry entry =   (Map.Entry)i.next();
      14               if(key.equals(entry.getKey())){
      15                   assertEquals(value,entry.getValue());
      16                return;
      17             }
      18         }
      19         fail("Iterator   didn't contain "+ key);
      20     }
      21 }
      当没有达到预期目的时使用 fail()方法,强制测试失败。
      ③避免使用sleep方法浪费大量的测试时间
      counterAccessFromMultipleThreads 用来测试一个多线程计数器,开启10个线程,每个线程调用计数器1000次,sleep(500),是为了让主线程等待开启的10个线程执行完毕
      那么问题来了,如果在10毫秒内所有线程都执行完毕,岂不白白浪费了490毫秒?又或者在等待500毫秒后仍有线程没有执行完毕,那该怎么办?    

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标软件测试之测试技术频道!

本文由 @凌雪 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程