`
feikiss
  • 浏览: 100052 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

OOCamp--测试驱动开发

 
阅读更多
   现在有类似这样一个需求:需要提供一个简单类库,以供其他开发者调用。现在进行Tasking,最简单的需求,这个类中应该拥有一个value记录长度值,也应该有一个单位unit来记录相应的单位,对于一个length对象来说,用户只关心我拿到这个对象后怎么用,比如,我两个对象可以比较是否相等,是否可以相加,对于其length的value和unit来说,也许用户并不关心他们的行为(至少现在是这样的),所以完全没必要为也不应该其提供相应的getter/setter 方法。现在我们来实现两个Length对象比较是否相等的行为。

  下面的测试用例我们很容易想到:
  1. 1m = 1m
  2. 1m = 100cm
  3. 1m != 2m
  4. 1m = 1000mm

   接着我们就需要开始写测试代码了:
import org.junit.Test;

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
public class LengthTest {

    @Test
    public void should_1_m_equals_1_m(){
        Length length1 = new Length(1,"m");
        Length length2 = new Length(1,"m");
        assertThat(length1.equals(length2),is(true));
    }
}

这时候编译是不通过的,因为我们就没有Length类,不过IDE能够很快的帮我们完成这件事儿,创建号Length类以后呢,跑测试,失败了。看来我们需要重写equals方法,为了让测试通过,我们可以先最简单的实现equals方法,代码如下:
public class Length {

    private int value;
    private String unit;
    public Length(int value, String unit) {
        this.value = value;
        this.unit = unit;
    }
    
    @Override
    public boolean equals(Object obj){
        Length anotherLength = (Length) obj;
        return (this.unit.equals(anotherLength.unit)&&this.value == anotherLength.value);
    }

}


好,接下来我们继续第二个测试用例:
  @Test
    public void should_1m_equals_100_cm(){
        Length length1 = new Length(1,"m");
        Length length2 = new Length(100,"cm");
        assertThat(length1.equals(length2),is(true));
        
    }

跑测试,失败。继续改equals方法:
    @Override
    public boolean equals(Object obj){
        Length anotherLength = (Length) obj;
        if(anotherLength.unit.equals("cm")){
            return this.value * 100 == anotherLength.value;
        }
        return (this.unit.equals(anotherLength.unit)&&this.value == anotherLength.value);
    }

运行,成功,然后依次类推,将余下的测试按照刚才的模式写完:
package com.lee.oocamp.blog;


import org.junit.Test;

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
public class LengthTest {

    @Test
    public void should_1_m_equals_1_m(){
        Length length1 = new Length(1,"m");
        Length length2 = new Length(1,"m");
        assertThat(length1.equals(length2),is(true));
    }
    
    @Test
    public void should_1_m_equals_100_cm(){
        Length length1 = new Length(1,"m");
        Length length2 = new Length(100,"cm");
        assertThat(length1.equals(length2),is(true));
        
    }
    @Test
    public void should_1_m_not_equal_2_m(){
        Length length1 = new Length(1,"m");
        Length length2 = new Length(2,"m");
        assertThat(length1.equals(length2),is(false));
        
    }
    @Test
    public void should_1_m_equals_1000_mm(){
        Length length1 = new Length(1,"m");
        Length length2 = new Length(1000,"mm");
        assertThat(length1.equals(length2),is(true));
        
    }
}

Length类中的equals方法代码:
    @Override
    public boolean equals(Object obj){
        Length anotherLength = (Length) obj;
        if(anotherLength.unit.equals("cm")){
            return this.value * 100 == anotherLength.value;
        }else if(anotherLength.unit.equals("mm")){
            return this.value * 1000 == anotherLength.value;
        }else{
            return this.value*1 == anotherLength.value;
        }
    }

运行单元测试,全部通过。兴奋之余,似乎少了些什么?是的!1m=100cm正确,但是验证100cm=1m了么?1000mm = 1m 似乎也没有验证?继续添加测试用例:

@Test
    public void should_1000_mm_equals_1_m(){
        Length length1 = new Length(1000,"mm");
        Length length2 = new Length(1,"m");
        assertThat(length1.equals(length2),is(true));
    }
    
    @Test
    public void should_100_cm_equals_1_m(){
        Length length1 = new Length(100,"cm");
        Length length2 = new Length(1,"m");
        assertThat(length1.equals(length2),is(true));
    }
    

运行,测试失败。为什么呢?因为我们只对this.value 做了从m向其他单位的转换,却并没有做从mm或cm向其他单位的转换。继续修改我们的实现代码,我们要让测试全部通过!

这时,我们想,我们既要由m向mm转换,又要由mm向m转换,为什么不在生成对象的时候就全部实现统一的转换呢?顺着这个思路我们可以继续往下走,由于有单元测试做保证,所以我们可以随意修改我们的实现。但是记着,改动不要太大,时刻记着运行单元测试,小步快跑是测试驱动开发的秘笈。

修改后Length代码如下:
public class Length {

    private int value;
    private String unit;
    public Length(int value, String unit) {
        this.value = getValue(unit,value);
        this.unit = unit;
    }
    
    private int getValue(String unit, int value) {
        int result = 0;
        if(unit.equals("m")){
            result = value * 1000;
        }else if(unit.equals("cm")){
            result = value * 10;
        }else if(unit.equals("mm")){
            result = value * 1;
        }
        return result;
    }

    @Override
    public boolean equals(Object obj){
        Length anotherLength = (Length) obj;
       return this.value == anotherLength.value;
    }

}

运行测试用例,全部通过!说明我们的测试是可行的,实现也是正确的。但是不和谐的因素出现了,在getValue中有太多的if-else 了!一个有良好设计风格的程序员肯定会想方设法的去消灭这些if-else。 好,我们继续重构(别忘了,我们有充分的单元测试做保证,因为我们的代码是由测试驱动出来的,只要测试通过了,代码就没问题,所以不要担心会把功能重构丢了。)getValue中的unit其实可以用枚举变量来代替,重构后代码清单如下:
Length类:
public class Length {

    private int value;
    private Length() { 
        
    }

    public static Length createLength(int value, UNIT unit) {
        Length length = new Length();
        length.value = unit.getTheValue(value);
        
        return length;
    }

    @Override
    public boolean equals(Object obj){
        boolean result = false;
        Length anotherLength = (Length) obj;
        result = this.value == anotherLength.value;
        return result;
    }
}

枚举UNIT:
public enum UNIT {
    M(1000),CM(10),MM(1);
    int radio;
    
    UNIT(int radio){
        this.radio = radio;
    }
    
    public int getTheValue(int value) {
        int result = value * this.radio;
        return result;
    }
}

当然,由于构造器设置为了私有的,Length由简单对象工程来生成,我们也需要修改我们相应的单元测试用例。修改完成后我们发现测试类中也存在大量重复性代码,是时候对测试类进行重构了,重构后代码如下:
import org.junit.Test;

import com.lee.oocamp.Length;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
public class LengthTest {

    @Test
    public void should_1_m_equals_1_m(){
        compareTwoLengthObj(1,UNIT.M,1,UNIT.M,true);
    }
    
    @Test
    public void should_1_m_not_equal_2m(){
        compareTwoLengthObj(1,UNIT.M,2,UNIT.M,false);
    }
    
    @Test
    public void should_1_m_not_equal_1cm(){
        compareTwoLengthObj(1,UNIT.M,1,UNIT.CM,false);
    }

    @Test
    public void should_1_m_equals_100cm(){
        compareTwoLengthObj(1, UNIT.M, 100, UNIT.CM ,true);
    }
    
    @Test
    public void should_1_m_equals_1000mm(){
        compareTwoLengthObj(1, UNIT.M, 1000, UNIT.MM ,true);
    }
    
    @Test
    public void should_100_cm_equals_1m(){
        compareTwoLengthObj(100, UNIT.CM, 1, UNIT.M ,true);
    }
    
    @Test
    public void should_1000_mm_equals_1m(){
        compareTwoLengthObj(1000, UNIT.MM, 1, UNIT.M ,true);
    }
    @Test
    public void should_2000_mm_not_equals_1m(){
        compareTwoLengthObj(2000, UNIT.MM, 1, UNIT.M ,false);
    }
    
    private void compareTwoLengthObj(int valueOfLength1,UNIT unitOfLength1,int valueOfLength2,UNIT unitOfLength2,boolean expect) {
        Length length1 = Length.createLength(valueOfLength1,unitOfLength1);
        Length length2 = Length.createLength(valueOfLength2,unitOfLength2);
        assertThat(length1.equals(length2),is(expect));
    }
    
}

即使又新加了几个测试用例,是不是看着也更清爽了呢?

end。
1
0
分享到:
评论

相关推荐

    使用 Simulink(R) 在 AWGN 信道上执行带穿孔的软判决维特比解码.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。 替换数据可以直接使用,注释清楚,适合新手

    极化码的高斯近似过程,基于matlab平台.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。 替换数据可以直接使用,注释清楚,适合新手

    广东省关于人工智能赋能千行百业的若干措施.docx

    广东省关于人工智能赋能千行百业的若干措施.docx

    湖北省数据条例(草案)(征求意见稿).docx

    湖北省数据条例(草案)(征求意见稿).docx

    CSS网站布局与开发技巧(pdf电子书)最新版本

    中软国际IT培训中心的培训教程,属于学习CSS网页设计的基础入门教程,讲一些原理和概念,高深的理论不太多。

    Python 中数据结构和算法的最少示例.zip

    Python Data Structures and Algorithms Minimal and clean example implementations of data structures and algorithms in Python 3. Contribute Thank you for your interest in contributing! There are many ways to contribute to the project. Start testing from here Take note when running all tests using unittest $ python3 -m unittest discover tests To run some specific tests you can do the following (e.g. sort) $ python3 -m unittest tests.test_sort Run all tests using pytest Make a note when $ python3 -m

    TeamIDE-win-2.6.31Team IDE 集成MySql、Oracle、金仓、达梦、神通等数据库、SSH、FTP、Redis、Zookeeper、Kafka、Elasticsearch、M

    TeamIDE-win-2.6.31Team IDE 集成MySql、Oracle、金仓、达梦、神通等数据库、SSH、FTP、Redis、Zookeeper、Kafka、Elasticsearch、M

    C/C++/Python/Java四种语言下的算法实现资源概览

    内容概要:本文综述了C、C++、Python、Java这四种主流编程语言中,用于实现常见和高级算法的学习资料,覆盖范围广泛,从书籍、在线课程平台到GitHub上的开源代码仓库均有提及。每种语言都详述了推荐的学习资源及其优势,旨在满足不同程度学习者的需要。 适合人群:对算法实现有兴趣的学生、自学爱好者、开发者等。 使用场景及目标:帮助读者挑选合适的语言和资源深入理解算法的理论与实际编码技巧,适用于个人提升、项目实践或教学使用。 其他说明:文章提供了丰富的学习渠道和实战项目,既适合作为基础理论的学习,也适合于实际操作练习,尤其强调通过实做加深理解的重要性。

    aiuiphone0000000000000000000

    aiuiphone0000000000000000000

    支持多场景回调开箱即用 原生仿百度登录验证.zip

    支持多场景回调开箱即用 原生仿百度登录验证.zip

    2023 年“泰迪杯”数据分析技能赛B题-企业财务数据分析与造假识别

    2023 年“泰迪杯”数据分析技能赛B题-企业财务数据分析与造假识别 完整代码

    Levenshtein Python C 扩展模块包含用于快速计算 Levenshtein 距离和字符串相似度的函数.zip

    Levenshtein Python C 扩展模块包含用于快速计算 Levenshtein 距离和字符串相似度的函数内容需要维护者介绍文档执照历史源代码作者需要维护者我 (Mikko Ohtamaa) 目前不维护此代码。我只是为了方便起见才将其拉到 Github 上的(之前在公共存储库中不可用)。因此,如果您提交了任何问题,我都不会调查。介绍Levenshtein Python C 扩展模块包含用于快速计算的函数Levenshtein(编辑)距离和编辑操作字符串相似度近似中位数字符串,以及一般字符串平均值字符串序列和集合相似度它同时支持普通字符串和 Unicode 字符串。需要 Python 2.2 或更新版本。StringMatcher.py 是一个基于 Levenshtein 构建的类似 SequenceMatcher 的示例类。它缺少一些 SequenceMatcher 的功能,但又有一些额外的功能。Levenshtein.c 也可以用作纯 C 库。您只需在编译时定义 NO_PYTHON 预处理器符号 (-DNO_PYTH

    基于OpenCV像素检测的Onmyoji游戏脚本

    基于OpenCV像素检测的Onmyoji游戏脚本

    机器人算法的 Python 示例代码 .zip

    Pythonbot高斯网格图射线投射网格图激光雷达至网格地图k-均值对象聚类矩形接头大满贯迭代最近点 (ICP) 匹配FastSLAM 1.0路径规划动态窗口方法基于网格的搜索Dijkstra 算法A* 算法D*算法D* Lite 算法位场算法基于网格的覆盖路径规划国家网格规划偏极采样车道采样概率路线图(PRM)规划快速探索随机树(RRT)回程时间*RRT* 和 reeds-shepp 路径LQR-RRT*五次多项式规划Reeds Shepp 规划基于LQR的路径规划Frenet 框架中的最佳轨迹路径追踪移动到姿势控制斯坦利控制后轮反馈控制线性二次调节器 (LQR) 速度和转向控制模型预测速度和转向控制采用 C-GMRES 的非线性模型预测控制手臂导航N关节臂对点控制带避障功能的手臂导航航空导航无人机三维轨迹跟踪火箭动力着陆双足动物倒立摆双

    可信任的企业4.0生态系统.pptx

    可信任的企业4.0生态系统.pptx

    C语言档案管理系统 代码完整

    学生信息包括:学号,姓名,年龄,性别,出生年月,地址,电话,E-mail等。试设计一学生信息管理系统,系统提供菜单方式作为人机界面并具有如下功能: 学生信息录入功能 学生信息浏览功能 按学号、姓名等进行查询、排序功能 2、要求界面简单明了;对输入的数据具有有效性检查能力,比如输入的成绩不在0~100之间,要求重新输入;

    原生js谷歌网页电吉他弹奏源码.rar

    原生js谷歌网页电吉他弹奏源码.rar

    原生js微信分享到朋友圈浮动层代码.zip

    原生js微信分享到朋友圈浮动层代码.zip

    第7章 聚类算法 - 作业 - 副本.ipynb

    第7章 聚类算法 - 作业 - 副本.ipynb

Global site tag (gtag.js) - Google Analytics