这是《构建之法》实战教学的一部分。适合作为同学们的第二个程序作业。

第一个程序作业: 请看 “概论” 一章的练习,或者老师的题目,例如这个。

作业要求:

软件工程的作业越来越有意思了, 我们在第一个作业中,用各种语言实现了一个命令行的四则运算小程序。 我们看看如果要把我们的小程序升级为能稳定运行,解决用户问题的软件,应该怎么做。

建议在做下面的题目的时候,采用结对编程的方式, 在练习中,让同学们学会模块化编程,信息隐藏,接口设计,TDD,等。

大家写了不少四则运算的练习,这些代码都各有特色,大家写的 “软件” 也有一定的用处。  如果我们要把这个功能放到不同的环境中去 (例如,命令行,Windows 图形界面程序,网页程序,手机App),  就会碰到困难,  因为目前代码的普遍问题是代码都散落在 main() 函数或者其他子函数中,我们很难把这些功能完整地剥离出来,作为一个独立的模块满足不同的需求。

我们看到,不同的代码解决不同层面的问题,有些是内部数据的计算 (例如四则运算);有些是和用户输入相关的 (例如 scanf,cin,图形界面的输入输出),有些是和数据的展现相关的 (例如 printf ,cout,println,DrawText),有些是和程序所在平台的架构相关的(例如 main 函数,程序倒计时的实现等)。 这就需要我们对软件的架构做一些整理和优化。

建议大家把四则运算的计算功能包装在一个模块中 (这个模块可以是一个类 Class,  一个DLL,等等), 为了方便起见,我们叫它 “计算核心” 模块, 这个模块至少在两个地方可以使用:

  1. 测试程序,这个可以是一个命令行的程序,或者是JUnit 的框架,或者是Visual Studio 单元测试的框架。这样,我们在算法层级保证了这个模块的正确性。

  2. 实际的软件,这是交付给最终用户的软件,有一定的界面和必要的辅助功能。

那么这个“计算核心”模块和使用它的其他模块之间是什么关系呢?  它们要通过一定的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相关推荐

  1. tdd 单元测试_何时给定在单元测试和TDD中的重要性

    tdd 单元测试 最近,我一直在写与自动测试有关的更高级的概念(主要与Spock有关). 但是,在进行测试培训时,我清楚地看到,通常对特定工具的知识并不是主要问题. 即使使用Spock,也可以编写肿且 ...

  2. 在单元测试和TDD中指定时间的重要性

    最近,我一直在写与自动测试有关的更高级的概念(主要与Spock有关). 但是,在进行测试培训时,我清楚地看到,通常对特定工具的知识并不是主要问题. 即使使用Spock,也可以编写肿且难以维护的测试,从 ...

  3. 原创 | 使用JUnit、AssertJ和Mockito编写单元测试和实践TDD (一)什么是单元测试

    If builders built buildings the way programmers wrote programs, then the first woodpecker that came ...

  4. 学习TDD:TDD的好处

    TDD的全称是Test Driver Development,测试驱动开发.就是开发要以测试为驱动.编码之前,测试先行.代码都没有,我如何测试,我连要测的对象都没有啊?这好像是个问题. TDD的哲学为 ...

  5. 单元测试:如何编写可测试的代码及其重要性

    原文来自互联网,由长沙DotNET技术社区编译.如译文侵犯您的署名权或版权,请联系小编,小编将在24小时内删除.限于译者的能力有限,个别语句翻译略显生硬,还请见谅. 作者:谢尔盖·科洛迪(SERGEY ...

  6. python单元测试框架作用_Python自动单元测试框架

    简介: 软件的测试是一件非常乏味的事情,在测试别人编写的软件时尤其如此,程序员通常都只对编写代码感兴趣,而不喜欢文档编写和软件测试这类"没有创新"的工作.既然如此,为什么不让程序员 ...

  7. 北航2022软件工程第三次作业——结对编程(最长英语单词链)

    软件工程第三次结对编程作业 项目 内容 这个作业属于哪个课程 北京航空航天大学2022春季软件工程(罗杰 任健) 这个作业的要求在哪里 结对编程项目-最长英语单词链 我在这个课程的目标是 学习软件工程 ...

  8. 「敏捷架构」核心实践:测试驱动开发(TDD)简介

    测试驱动开发(TDD) 是一种渐进的开发方法,它结合了测试优先的开发,即在编写足够的产品代码以完成测试和重构之前编写测试.TDD的主要目标是什么?一个观点是TDD的目标是规范而不是验证(Martin, ...

  9. php 单元测试分享,今日分享:代码整洁之道- 单元测试

    从开发的角度来讲,先把变量.函数按照一定的命名.格式组织好,接下来,开始编写代码,在业界,很多提倡测试驱动开发,接下和大家聊一下单元测试. TDD是测试驱动开发(Test-Driven Develop ...

  10. TDD代码驱动测试基础

    测试驱动开发(TDD)知识调研 文章目录 测试驱动开发(TDD)知识调研 TDD的核心目标 处理遗留代码问题的核心法则如下. 不良测试的死亡漩涡(亦称为 SCUMmy 周期) 警惕撤回迈入死亡漩涡的步 ...

最新文章

  1. 惊!!!CV界的BERT要来了?准确率提高近25%!
  2. struts2注解(转)
  3. 【运筹学】线性规划数学模型 ( 单纯形法 | 最优解判定原则 | 单纯形表 | 系数计算方法 | 根据系数是否小于等于 0 判定最优解 )
  4. 【正一专栏】曼城攻击力惊人露出冠军相
  5. 白鹭引擎开发飞机大战详尽教程(四控制飞机移动)
  6. 一个 redis 异常访问引发 oom 的案例分析
  7. SpringMvc之参数绑定注解详解之一
  8. 20172311 2017-2018-2 《程序设计与数据结构》实验一报告
  9. ReactiveObjC(OC版ReactiveCocoa)的安装与使用
  10. 收藏 不显示删除回复显示所有回复显示星级回复显示得分回复 为什么有时候ASP在插入一条记录时,它会在数据里面插入两条一样的记录?...
  11. 3、简单了解Angular应用的启动过程
  12. DirectX 9高层着色语言介绍4——语言基础(3)
  13. 150.1 go语言开发实战慕课版
  14. 用python爬取qq空间内容_Python爬取qq空间说说的实例代码
  15. 想要更高效地使用云计算,推荐学习云计算部署的五大策略
  16. 第三届国际金融科技论坛开幕,神州信息专家参与蓉城“论道”
  17. Linux设备驱动开发详解【二】_设备驱动相关硬件基础知识
  18. 这次财报,同程艺龙又沾了腾讯的光
  19. IE6 WEB开发调试插件:IE Developer Toolbar
  20. postgresql数据库连接,增删改查数据

热门文章

  1. js 简单弹框toast
  2. js中this的指向问题
  3. Tomcat基础教程(一)
  4. js控制文本框中的字符数
  5. vs2010 学习Silverlight学习笔记(8):使用用户控件
  6. 转--javascript 数组
  7. 操作系统学习笔记-01-操作系统的概念(定义),功能和目标
  8. SpringBoot集成MyBatis的分页插件PageHelper
  9. 【剑指offer】面试题23:链表中环的入口节点
  10. java c 基本类型_java 基本数据类型