软件工程练习:模块化,单元测试,回归测试,TDD
这是《构建之法》实战教学的一部分。适合作为同学们的第二个程序作业。
第一个程序作业: 请看 “概论” 一章的练习,或者老师的题目,例如这个。
作业要求:
软件工程的作业越来越有意思了, 我们在第一个作业中,用各种语言实现了一个命令行的四则运算小程序。 我们看看如果要把我们的小程序升级为能稳定运行,解决用户问题的软件,应该怎么做。
建议在做下面的题目的时候,采用结对编程的方式, 在练习中,让同学们学会模块化编程,信息隐藏,接口设计,TDD,等。
大家写了不少四则运算的练习,这些代码都各有特色,大家写的 “软件” 也有一定的用处。 如果我们要把这个功能放到不同的环境中去 (例如,命令行,Windows 图形界面程序,网页程序,手机App), 就会碰到困难, 因为目前代码的普遍问题是代码都散落在 main() 函数或者其他子函数中,我们很难把这些功能完整地剥离出来,作为一个独立的模块满足不同的需求。
我们看到,不同的代码解决不同层面的问题,有些是内部数据的计算 (例如四则运算);有些是和用户输入相关的 (例如 scanf,cin,图形界面的输入输出),有些是和数据的展现相关的 (例如 printf ,cout,println,DrawText),有些是和程序所在平台的架构相关的(例如 main 函数,程序倒计时的实现等)。 这就需要我们对软件的架构做一些整理和优化。
建议大家把四则运算的计算功能包装在一个模块中 (这个模块可以是一个类 Class, 一个DLL,等等), 为了方便起见,我们叫它 “计算核心” 模块, 这个模块至少在两个地方可以使用:
测试程序,这个可以是一个命令行的程序,或者是JUnit 的框架,或者是Visual Studio 单元测试的框架。这样,我们在算法层级保证了这个模块的正确性。
实际的软件,这是交付给最终用户的软件,有一定的界面和必要的辅助功能。
那么这个“计算核心”模块和使用它的其他模块之间是什么关系呢? 它们要通过一定的API (Application Programming Interface) 来和其他模块交流。 这个API 接口应该怎么设计呢? (这是一个给有一定经验和实力的同学的题目), 为了简单,我们可以从下面的最简单的接口开始:
Calc()
这个Calc 函数接受字符串的输入(字符串里就是运算式子,例如 “ 5+3.5“, “7/8 – 3/8 ”, “3 + 90 * (-0.3)“ 等等),这个模块的返回值是一个字符串,例如,前面几个例子的结果就是 ( ”17.5“, “ 1/2”, “-24“).
假设我们用的是类,我们的测试程序刚开始可以是非常简单的测试例子: (用伪代码表示)
String result = Core.Calc(“1 + 1”) ;
Assert ( result == “2”); //我们断言 1 + 1 的结果一定是 2.
然后同学们实现自己 Core 的这个功能。
第一阶段目标 - 能把计算的功能封装起来,通过测试程序和API 接口测试其简单的加法功能。
加法成功之后,然后我们再做减法, 乘法,除法,我们假设目前为止都是两个操作数的运算,还是很容易实现的。 由于同学们已经在自己以前的程序中实现了各种算法,这时候只要把实现的算法搬过来就好了。 大家可以不断增加测试的数量,在每实现一个新的功能的时候,要保证以前运行正确的例子继续是正确的, 通过这样的 “回归测试“, 来保证自己实现的函数一直是正确的 (请看书中关于单元测试,回归测试的内容)。
第二阶段目标 - 通过测试程序和API 接口测试其简单的加减乘除功能。并能看到代码覆盖率。
然后,更欢乐的情况出现了, 多个运算符的运算,带负数的运算。
啊,等一下,如果我们考虑这些情况的话, 我们这个模块有一些参数要设置,例如,最多几个运算符,能带括号么?数据范围是多少,还要设置计算的精度(保留几位小数,必须是用分数形式表示么,等等), 这是由什么API 来决定呢? 我们可以扩展 Calc() 的定义,让它接受一个新的参数 “precision”, 或者我们可以启用一个新的函数 Setting()。
如果我想表示:
最多4 个运算符
数值范围是 -1000 到 1000
精度是小数点后两位
怎么通过API 告诉我们的模块呢? 我们当然可以用函数的参数直接传递,但是参数的组合很多,怎么定义好参数的规范呢? 建议大家考虑用 XML 来传递这些参数。
增加了新的Setting() 函数之后,我们要让模块支持这样的参数,同时,还要保证原来的各个测试用例继续正确地工作。
第三阶段目标 - 通过测试程序和API 接口测试对于各种参数的支持。并能看到代码覆盖率。
这个时候,如果输入是有错误的,例如 “1 ++ 2”, 在数值范围是 -1000 .. 1000 的时候,传进去 “10000 + 32768 * 3”, 或者是 “ 248.04 / 0” 怎么办? 怎么告诉函数的调用者 “你错了”? 把返回的字符串定义为 “-1” 来表示? 那么如果真的计算结果是 “-1” 又怎么处理呢?
建议这个时候,我们要定义各种异常 (Exception), 让 Core 在碰到各种异常情况的时候,能告诉调用者 - 你错了! 当然,这个时候,我们同样要进行下面的增量修改:
定义要增加什么功能 - 例如:支持 “运算式子格式错误” 异常
写好测试用例,传进去一个错误的式子,期望能捕获这个 异常。 如果没有,那测试就报错。
在 Core 模块中实现这个功能
测试这个功能
同时测试所有以前的功能,保证以前的功能还能继续工作 (没有 regression)
确认功能完成,继续下一个功能
第四阶段目标 - 界面模块,测试模块和核心模块的松耦合。
既然各组各组同学都写了高质量的各个模块,而且模块之间的关系是明确定义的,一致的,那么,小组A 的测试模块就可以测试小组B 的核心模块;小组C 的用户界面模块就可以和小组B 的核心模块结合起来,正常运行。对吧?! 那我们就让两个小组 (A,B) 在一起,测试一下下面的情况:
- A 的核心模块, 加上B 的测试模块和用户界面模块
- B 的核心模块,加上A 的测试模块和用户界面模块
两组同学分析合并之后出现了什么问题,为何会出现这样的问题?如何改进? 并且改进各种模块中的 bug
(请看北航同学的作业心得:https://www.cnblogs.com/SivilTaram/p/4859934.html )
第五阶段目标 - 通过增量修改的方式,改进程序, 完成对各种错误情况的处理。
选择两组程序中高质量的模块,增加必要的功能,把所有代码签入源代码管理服务器, 同时,把这个软件发布出来。
(请看北航一个同学的尝试:https://www.cnblogs.com/skyxuan/p/4858486.html)
第六阶段目标 - 如何把这些模块重用到别的相关项目中去。
例如,现在大家要做一个 【24点游戏】:
有四个数字,通过加减乘除括号等四则运算,把四个数字组成一个算式,结果是 24。 这个游戏有两档难度,入门级:数字在 1..10 之间; 高级:数字在 1..99 之间
可用命令行或本地GUI 或 网页界面的方式实现这个游戏
我们要从头开始写所有的程序么?这个题目需要的一些功能和我们花了很长时间做的 “四则运算” 模块有不少类似之处, 那么,如何把现有模块经过少量的改动,快速地构建成为一个 24 点游戏所需的模块呢? 做到这些,并能总结一些规律,软件工程课程就有点摸到 “工程” 的边了!
软件工程练习:模块化,单元测试,回归测试,TDD的更多相关文章
- 邹欣,现代软件工程讲义:单元测试&回归测试
http://www.cnblogs.com/xinz/archive/2011/11/20/2255830.html 邹欣, 现代软件工程讲义 2 开发技术 - 单元测试 & 回归测试
- 软件工程:vs单元测试
vs单元测试?VS?没装呢... 那么赶紧装个吧,于是跑到这去了: http://www.msdn.hk 我下个免费社区版. 安装过程没有什么需要说明的,傻瓜式安装会吗?当然中间会耗很长时间. 由于以 ...
- 软件工程概论---max单元测试
题目:一个单元测试,查找list[]中的最大值 编写一个程序对Largest函数进行测试,列举所有测试用例. 思路:首先确保数组不为空,和数组长度不为0,否则输入错误.根据老师所给的函数写一个主函数, ...
- Python 在Visual studio 中做单元测试进行TDD开发
Unit Tests Steve Dower edited this page on 14 Jul · 3 revisions Pages 38 Home Azure Remote Debugging ...
- 敏捷测试(1)--TDD概念
题记 本系列笔记将从测试人员的角度,总结在百度两年来的测试经验,记录一个完整的基于敏捷流程的验收测试全过程,分享在测试过程中的一些知识和经验,以及自己的一些理念.总结自己,也希望对大家有益. 概念 验 ...
- c++ 单元测试框架 gmock 深度剖析
c++ 单元测试框架 gmock 深度剖析 随着微服务和CI的流行,在目前的软件工程领域中单元测试可以说是必不可少的一个环节,在TDD中,单元测试更是被提高到了一个新的高度.但是很多公司由于很多不同的 ...
- 软件工程(QLGY2015)第一次作业小结(含成绩)
相关博文目录: 第一次作业点评 第二次作业点评 第三次作业点评 Github项目提交 github的代码提交,大部分人都只是提交了单个文件,存在几个问题 请提交完整的项目文件到github 问题:为什 ...
- Asp.net MVC 单元测试 简要笔记
首先要啰嗦几句. 单元测试是TDD的重要实践方法,也是代码质量的一种保证手段.在项目的工程化开发中,研发人员应该尽量保证书写Unit Test,即使不使用TDD. (VS中,我们可以直接使用微软提供的 ...
- 编写基于Property-based的单元测试
编写基于Property-based的单元测试 作为一个开发者,你可能认为你的职责就是编写代码从而完成需求.我不敢苟同,开发者的工作是通过软件来解决现实需求,编写代码只是软件开发的其中一个方面,编写可 ...
- 测试计划驱动开发模式 TPDD:一种比 TDD 更友好的开发模式
相信大部分开发团队都在使用TDD,并且还有很多开发团队都 对外声明 在使用 TDD 开发模式. 之所以说是“对外声明”,是因为很多开发团队虽然号称使用的是 TDD 开发模式,实际开发过程中却无法满足 ...
随机推荐
- JavaScript中解决计算精度丢失的问题
在做项目之前老师就给我们封装好了一个js文件,解决计算中丢失精度的一些函数,直接引用js文件就可以使用. eg: var numA = 0.1; var numB = 0.2; alert( numA ...
- element-ui 2.4.8 BUG 标签页的最后一个Tab标题没法移除,更新后发现最新版本不存在该问题了 记录下
- kmp算法python实现
kmp算法python实现 kmp算法 kmp算法用于字符串的模式匹配,也就是找到模式字符串在目标字符串的第一次出现的位置比如abababc那么bab在其位置1处,bc在其位置5处我们首先想到的最简单 ...
- windows平台下实现高可用性和可扩展性-ARR和HLB
本文档提供了关于如何将应用程序请求路由(ARR)与硬件负载均衡器一起使用以实现高可用性和可伸缩性的说明性指导.本文采用F5大IP负载均衡器来说明ARR与硬件负载平衡器之间的工作关系. IIS7.0及以 ...
- Lingo求解线性规划案例1——生产计划问题
凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ 说明: Lingo版本: 某工厂明年根据合同,每个季度末 ...
- ES5-ES6-ES7_Generator 函数
Generator函数的概述 Generator 函数是 ES6 提供的一种异步编程解决方案 Generator函数指的是一种新的语法结构,是一个遍历器对象生成器,它内部可以封装多个状态,很适合用于异 ...
- php面试题整理(三)
判断是不是ie浏览器 1,1
- SQL FULL JOIN 关键字
SQL FULL JOIN 关键字 只要其中某个表存在匹配,FULL JOIN 关键字就会返回行. FULL JOIN 关键字语法 SELECT column_name(s) FROM table_n ...
- css3 object-fit详解
上传头像的时候遇到了头像变形的问题,最后通过object-fit: cover完美解决了.这个CSS属性可以达到最佳最完美的居中自动剪裁图片的功能. object-fit理解 CSS3 backgro ...
- 利用filter替换字符串中的空格
s = "abc def ghi xy" print(','.join(filter(lambda x: x, s.split(' '))))