- 良好的设计应当是所有开发者的追求,然而对于遗留代码来说,良好的设计只是我们不断逼近的目标。
---我们通常先遇到是的已有的Project和产品代码,我们要对其做应用修改,开发新的功能,适应不同客户的需求。
为什么一开始总是这样,而不是从无到有?
- Seam,fake/mock object,解依赖技术
---总结了三大类方法,使测试代码独立于产品代码之外。
- 介绍的修改软件的四个起因,在过去的月度release的流程都遇到过的问题。这些都是客户需求的变化。
1. 添加新特性---New feature introduced
2. 修正bug---Bug fixed
---这两者都有对代码的修改,客户通常会认为这都是功能上的问题,而开发会从代码修改上区分是新的行为还是bug的修正。开发之所以要区分:毕竟bug是task没有做好,而新功能是新的task。
3. 改善设计---Feature enhancement
重构(refactoring):在不改变软件行为的前提下改善其设计的举动
理念:1) 编写测试已确保现有行为不变
2) 进行一系列的结构上的调整,并通过测试的支持使得代码修改更容易着手
3)有可能会损失一部分性能
4. 优化资源使用---Peformance improvement
优化-时间与内存
- Matrix table (结构、功能、资源使用) for software modification cause:
添加特性 | 修正bug | 重构 | 优化 | |
结构 | 改变 | 改变 | 改变 | -- |
新功能 | 改变 | -- | -- | -- |
功能 | -- | 改变 | -- | -- |
资源使用 | -- | -- | -- | 改变 |
结论:添加特性、重构、优化都维持既有功能不变。
- 修改代码为了减小风险所要考虑的三个问题,自我检查。
1)我们要进行哪些修改
2)我们如何得知已经正确地完成了修改
3)我们如何得知没有破坏任何(既有的)东西
=============================================================
- 测试含义的衍生
通过测试来检验正确性---通过测试代码确认既定功能的正确性。
通过测试来检测变化---通过测试代码发现功能修改的变化,确认其合理性(行为改变 or 引入Bug)。
- 回归性测试---周期性的运行测试来检验已知良好行为---软件夹钳
---在自动化daily run可以很快发现开发修改产生的问题。
- 单元测试---最为原子的行为单元(过程:函数,面向对象:类)
---确定最小单元,使测试具备正交性。函数,接口,类方法调用,能够确定输入的测试数据和输出测试结果。
- 单元测试的具备的品质
1)运行块
2)能帮助我们定位问题所在
- 单元测试与大型测试
---测试分组:测试模块化,树形分层,可选择的测试策略。
- 单元测试的效率
要求:十分之一秒对于单元测试来说简直就像一个世纪一样
---测试类的数量,或许以后就会碰到的问题。
- 遗留代码处理过程:解除依赖性以便改动变得更容易
---遗留代码修改算法
1)确定改动点
2)找出测试点
3)解依赖
4)编写测试
5)修改、重构
- TDD来往遗留代码中添加特性
---测试数据的增加导致测试时间的增加,测试数据模拟测试场景。
- 感知---选择可以去依赖的类;分离---寻找一定的方法去除依赖
- 源代码集成需要测试的类,重写内部的定义,并简化内部依赖,调用fake object和mock object,Mock object就是在内部进行断言检查的伪对象。
---fakeObject--->mockobject
Example:
--------------------------------------------------------
//需要测试的接口类;
class InterfaceCalss{
public:
Method();
};
--------------------------------------------------------
//接口类实现产品类定义的具体功能;
class ProductClass public: InterfaceCalss{
public:
CalssA m_ObjA;
CalssB m_ObjB;
CalssC m_ObjC;
};
---------------------------------------------------------
//定义虚拟类,用来解除对部分的代码调用依赖;
class FakeClass{
};
---------------------------------------------------------
class TestClass public:InterfaceCalss{
public:
//调用虚拟类,解除依赖
FakeClassA m_ObjA;
FakeClassB m_ObjB;
FakeClassC m_ObjC;
//获取最后的结果反馈;
int GetLastRresultMethod();
};
//TestClass相当于ProductClass一个测试副本
----------------------------------------------------------------
- 非面向对象语言中,则可以通过定义一个替代函数来达到伪装的目的,改替代函数将某些值记录在全局数据结构中,从而我们可以在测试中访问这些值。
---预处理期接缝,宏替换。