`
feikiss
  • 浏览: 100371 次
  • 性别: 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
分享到:
评论

相关推荐

    基于C++开发的WEB服务器,支持C/C++、Python、Java等多语言混合开发WEB应用

    基于C++开发的WEB服务器,支持C/C++、Python、Java等多语言混合开发WEB应用

    基于STM8单片机的TM1615七段数码管驱动(模拟I2C).zip

    基于STM8单片机的编程实例,可供参考学习使用,希望对你有所帮助

    基于STM8单片机的电位器输入控制继电器输出实验.zip

    基于STM8单片机的编程实例,可供参考学习使用,希望对你有所帮助

    网络安全CTF靶场之rce-labs

    该靶场仅供学习使用!

    python爬虫豆瓣电影TOP250,以及数据化分析程序源代码+数据

    电影详情链接 图片链接 影片中文名 影片外国名 评分 评价人数 概况 相关信息 https://movie.douban.com/subject/1292052/ https://img2.doubanio.com/view/photo/s_ratio_poster/public/p480747492.jpg 肖申克的救赎   The Shawshank Redemption 9.7 2529468 希望让人自由 导演: 弗兰克·德拉邦特 Frank Darabont   主演: 蒂姆·罗宾斯 Tim Robbins ... 1994   美国   犯罪 剧情 https://movie.douban.com/subject/1291546/ https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2561716440.jpg 霸王别姬 9.6 1880353 风华绝代 导演: 陈凯歌 Kaige Chen   主演: 张国荣 Leslie Cheung 张丰毅 ........

    汽车中间件市场调研报告:2023年全球汽车中间件市场销售额达到了78亿美元

    汽车中间件市场调研报告:2023年全球汽车中间件市场销售额达到了78亿美元 在数字化转型的浪潮中,汽车中间件作为连接硬件与软件的关键桥梁,正引领着汽车行业的新一轮变革。随着全球汽车产业的快速发展,中间件市场规模持续扩大,展现出前所未有的增长潜力。然而,面对复杂多变的市场环境和不断涌现的新技术,企业如何精准把握市场脉搏,实现可持续发展?本文将深入探讨全球及中国汽车中间件市场的现状、趋势及竞争格局,为您揭示咨询的重要性。 市场概况: 根据QYResearch(恒州博智)的统计及预测,2023年全球汽车中间件市场销售额达到了78亿美元(约7803百万美元),预计2030年将达到156亿美元(约15630百万美元),年复合增长率(CAGR)为10.3%(2024-2030)。这一数据不仅彰显了中间件市场的强劲增长动力,也预示着未来巨大的市场空间。 技术创新与趋势: 随着自动驾驶、车联网等技术的不断发展,汽车中间件正面临着前所未有的技术挑战与机遇。新一代中间件需要具备更高的实时性、更低的延迟以及更强的数据处理能力,以满足复杂多变的汽车应用场景。同时,云计算、大数据、人工智能等技术的融合应用,将进

    python语言mp3pl爬虫程序代码QZQ.txt

    python语言mp3pl爬虫程序代码QZQ

    小语种字体TTF文件转PNG图片的方法

    # 小语种字体TTF文件转PNG图片的方法 ## 准备工作 1. 下载python3.9,推荐3.6~3.9,几个依赖包在这个版本运行的好。 2. 下载 FontForge-mingw-w64 ,可自行下载,或从文末打包好的工具包直接使用。 3. 下载需要导出的字体ttf文件,最好先装在本机系统上。 ## 导出方法 1. 把 `tts2png2.py` 文件复制到软件的bin目录下。 2. 修改 `tts2png2.py` 文件中的字体路径,注意Windows用双斜杠。 3. 从bin目录打开终端(管理员模式启动)。 4. 运行脚本 `./ffpython .\tts2png2.py`。

    26页-基于AI人工智能的智慧校园综合解决方案AI+智慧校园综合解决方案.pdf

    在21世纪的科技浪潮中,人工智能(AI)无疑是最为耀眼的明星之一,它以惊人的速度改变着我们的生活、工作乃至整个社会的运行方式。而在人工智能的广阔领域中,大模型(Large Models)的崛起更是开启了智能技术的新纪元,引领着AI向更加复杂、高效、智能的方向发展。本文将深入探讨人工智能大模型的内涵、技术特点、应用领域以及对未来的影响。 一、人工智能大模型的内涵 人工智能大模型,顾名思义,是指具有庞大参数规模和数据处理能力的AI模型。这些模型通过深度学习算法,在海量数据上进行训练,能够学习到丰富的知识表示和复杂的模式识别能力。与传统的小型或中型模型相比,大模型在理解自然语言、生成高质量内容、进行跨模态信息处理等方面展现出前所未有的优势。它们不仅能够执行特定的任务,如图像识别、语音识别,还能进行创造性的工作,如文本生成、音乐创作,甚至在某些情况下展现出接近或超越人类的智能水平。 二、技术特点 海量数据与高效训练:大模型依赖于庞大的数据集进行训练,这些数据涵盖了广泛的主题和情境,使得模型能够学习到丰富的语义信息和上下文理解能力。同时,高效的训练算法和硬件加速技术,如TPU(Tensor Processing Unit)和GPU,使得大规模模型的训练成为可能。 自注意力机制与Transformer架构:许多领先的大模型采用了Transformer架构,特别是其自注意力机制,这种设计使得模型在处理序列数据时能够捕捉到长距离依赖关系,极大地提高了模型的表达能力和泛化能力。 多任务学习与迁移学习:大模型通常具备多任务学习的能力,即在一次训练中同时学习多个任务,这有助于模型学习到更通用的知识表示。此外,迁移学习使得这些模型能够轻松适应新任务,只需少量额外数据或微调即可。

    下垂控制-基于T型三电平逆变器的下垂控制,电压电流双闭环,采用LCL滤波,SPWM调制方式 1.提供simulink仿真源文件 2.提供下垂控制原理与下垂系数计算方法 3.中点平衡控制,电压电流双闭环

    下垂控制-基于T型三电平逆变器的下垂控制,电压电流双闭环,采用LCL滤波,SPWM调制方式 1.提供simulink仿真源文件 2.提供下垂控制原理与下垂系数计算方法 3.中点平衡控制,电压电流双闭环控制 4.提供参考文献

    一个仿大众点评、美团的城市选择器,使用如同Rx一样优雅,并且UI和城市数据可以自定义.zip

    城市选择器一个仿大众点评的城市快速选择器, 最少只需 一行 代码即可启动城市选择器, 支持页面样式修改,多元化自定义截屏 版本日志V0.4.6优化地理位置设置时有时会设置不成功问题修复其他若干问题修改UI默认主题色V0.4.5修改设置位置信息方式,由之前必须在打开页面之前获取位置信息改为允许用户在打开页面后设置位置信息,具体使用方式见 Step3简化配置项,不需要在AndroidManifest中再注册Activity,并默认隐藏titlebarV0.4.3修复更新数据库表结构后第一次进入会闪退问题V0.4.0数据库表结构修改,增加了高德地图citycode设置gps城市的api略有改动见 Step3V0.3.3紧急修复一个可能导致内存泄漏问题优化提高滑动检索效率隐藏下拉刷新labelV0.3.1在搜索框后面添加一个清空搜索框按钮修复搜索框中输入空格会搜索出全部城市问题修复搜索结果弹出框中文字在不同theme下显示不同颜色问题,现在已统一为黑色其他调用时参数合法性校验V0.3.0简化api调用形式,修改为Rx形式,见操作步骤

    慢性病大数据分析处理.zip

    慢性病大数据分析处理慢性病项目

    Multisim单片机资源单片机C语言程序设计实训100例

    Multisim单片机资源单片机C语言程序设计实训100例提取方式是百度网盘分享地址

    PMSM永磁同步电机最大转矩电流比MTPA控制仿真,弱磁控制仿真,前馈补偿仿真程序,详细解析教程文档 这是一份非常完美的仿真文件及详细教程,从仿真效果图看转速、电流及转矩跟随非常稳定 该算法架构包

    PMSM永磁同步电机最大转矩电流比MTPA控制仿真,弱磁控制仿真,前馈补偿仿真程序,详细解析教程文档。 这是一份非常完美的仿真文件及详细教程,从仿真效果图看转速、电流及转矩跟随非常稳定。 该算法架构包含如下模块: 1)SVPWM矢量控制模块 2)转速环PI调节器、电流环PI调节器; 3)MTPA调节器; 4)弱磁控制器; 5)前馈补偿; 一份该仿真的算法说明文档,每一步都有详细介绍如何搭建,包括环路参数怎么算,拿来做毕设或者学习都很方便; 几篇参考文献; 一篇作者自己写的算法总结,让你少走弯路; 两个视频;

    手绘卡通儿童人物幼儿园教学课件模板.pptx

    手绘卡通儿童人物幼儿园教学课件模板

    Simulink电动汽车仿真模型(包含行驶阻力模型,工作模式切模型,驾驶员模型,PID控制模块等,NEDC,CLTC工况仿真结果)东西很全

    Simulink电动汽车仿真模型(包含行驶阻力模型,工作模式切模型,驾驶员模型,PID控制模块等,NEDC,CLTC工况仿真结果)东西很全

    2-Perfect Backup v3.3.0 全功能备份神器,支持多种备份方式,免费使用,可商用

    是一款全功能的备份软件,支持增量备份、差异备份、完全备份和同步备份,帮助用户轻松保护关键文件。支持多种存储选项,包括本地硬盘、USB驱动器、网络文件夹、云存储和 FTP 服务器等。 【使用方法】: 1. 下载并安装 Perfect Backup。 2. 打开软件,选择备份类型(增量、差异、完全或同步)。 3. 指定源文件和目标存储位置。 4. 设置备份计划,选择备份频率。 5. 点击“开始备份”,执行备份任务。

    基于STM8单片机的光敏电阻模拟量ADC输入(带中断).zip

    基于STM8单片机的编程实例,可供参考学习使用,希望对你有所帮助

Global site tag (gtag.js) - Google Analytics