测试技术之有效的单元测试
白羽 2019-01-07 来源 :网络 阅读 1020 评论 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个线程执行完毕


                   

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



本文由 @白羽 发布于职坐标。未经许可,禁止转载。
喜欢 | 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小时内训课程