一支函数多个前置条件,由于逻辑代码是顺序编写的,这些条件如果分开测试,就存在第二条件测试时,就要确保让第一个条件成立(哪怕用MOCK方式都很烦),第四个条件需要前面三个条件成立,前置条件越多就越烦,如何解决这个问题。
例:玩家进入游戏世界的逻辑。
(C++的语法,我尽量简化写,大家应该看得懂)
1、如果游戏世界人数超过1000,则进入失败。
2、如果playerName未找到,也就是没有创建过该player,则进入失败。
3、如果player所在的场景之前未添加到gameWorld,则进入失败。
4、如果该player已经进入游戏世界,则进入失败。
对应的类和成员函数简略为:
class GameWorld
{
public:
bool enterPlayer(String playerName)
{
}
private:
list<Player> m_playerExists;
};
现在我要为第4条逻辑编写测试代码,即:如果该player已经进入游戏世界,则进入失败。
一种写法是:
gameWorld.enterPlayer(“jack”) ;
CPPUNIT_ASSERT( !gameWorld.enterPlayer(“jack”) );
这种写法要求第一次进入世界要能够成功,但进入世界需要满足另外三个条件。第1个条件比较容易满足,而第2条件就需要通过mock确保”jack”已经创建过,第3条件就需要再向gameWorld添加jack所在的场景。而添加场景需要去满足场景添加的条件。这样一来,这个测试的准备工作很多。很多都是之前在编写第1、2、3逻辑时都已经写过的代码。为了满足测试的独立性,我四个逻辑的测试代码是分开的,这一来就出现了重复的代码。
第二种写法:
直接访问gameWorld的私有变量m_playerExists,添加一个名为”jack”的player对象。
这种写法最简单,特别在c++下只需要让测试类成为GameWorld的friend就可以访问私有变量了。但这种做法严重破坏了类的封装,如果已存在的玩家不是以list<Player>存放,重构成以map<string ,player>存放,那么测试代码也得改。照理类内部的代码重构不应该影响到测试代码才对。
第三种写法:
为GameWorld添加一支isPlayerIn()的函数
virtual bool isPlayerIn(String playerName)
{
}
然后对gameWorld进行mock。
class GameWorldMock : public GameWorld
{
protected:
virtual bool isPlayerIn(String playerName)
{
return true;
}
};
这种写法较第二种好一些,同时也驱动extract method,但同样也存在不妥的地方。isPlayerIn在实际代码中并没有用到,应该是private的,但为了让测试代码mock,要改成是protected。当然你会说isPlayerIn开放成public都行,正好可以做为enterPlayer的后置检测条件。这里我同样有一个困惑,测试中的后置检测条件往往需要通过一些get函数来提供,但常只在测试代码中才用到,实际代码中并没人调用。类似这样的检测函数很多,如果都public了,类的接口定义就很庞大。我一直提倡类的public函数越少越好,不必要的尽量不提供,这样调用者更容易调用。
其实不只是多个前置条件会遇到这个问题,很多添加、删除功能更是会遇到这个问题。删除一个对象的逻辑,就要求完成添加的所有准备,结果添加部分的代码被重复的测试。我的项目中,很从团队成员都干脆将所有测试写在一起,从添加到中间逻辑再到删除。结果一个测试上百行,不易读,不易修护。
分享到:
相关推荐
单元测试与TDD实践 **一、单元测试之测试目的** 单元测试,作为软件开发过程中的重要环节,其核心目标在于确保代码的质量、可维护性和可扩展性。它通过独立测试软件中的最小可测试单元,如函数或方法,来验证其...
在《测试驱动开发的三项修炼——走出TDD丛林》中,作者深入探讨了如何有效地实践TDD,以提高软件质量和开发效率。以下是关于TDD的三个关键修炼: 1. **理解测试金字塔**:测试金字塔是一个指导原则,它建议我们构建...
在《测试驱动开发的三项修炼——走出TDD丛林》中,作者深入探讨了TDD的核心理念、实践技巧以及常见误区,帮助开发者更好地理解和应用TDD。 一、TDD的基本原则 1. **红灯原则**:首先编写一个不能通过的测试用例...
### 工程与技术实践-TDD中常见的10大反模式 #### 1. 引言 测试驱动开发(Test-Driven Development, TDD)是一种软件开发方法论,它要求在编写任何生产代码之前,先编写相关的测试用例。TDD的核心原则包括“先测试...
《敏捷建模:极限编程和统一过程的有效实践》是一本深入探讨敏捷开发方法的书籍,主要聚焦于极限编程(XP)和统一过程(RUP)这两种广泛应用的敏捷框架。在这个快速变化的IT行业中,敏捷方法论已经成为软件开发的...
- **提高代码质量**:TDD迫使开发者思考边界条件和异常情况,有助于发现和修复潜在的bug。 - **设计上的指导**:测试用例作为需求的表达,有助于形成清晰的接口和职责划分。 - **减少回归错误**:频繁的自动化...
#### TDD——从故事开始:构建软件开发的新范式 测试驱动开发(Test-Driven Development,简称TDD),是一种敏捷软件开发方法,它强调在编写实际代码之前先编写测试用例。这种方法的核心理念在于“先写测试”,即在...
这本书以PDF英文版的形式提供,旨在帮助Java程序员深入理解和实践TDD方法论,提升软件开发的质量和效率。 测试驱动开发(TDD)是一种软件开发方法,强调在编写任何生产代码之前先编写测试。TDD的核心流程可以概括为...
测试驱动开发(TDD,Test-Driven Development)是一种软件开发实践,强调先编写测试用例,然后根据测试用例来编写实际的代码。TDD的基本流程包括:先思考要实现的功能,设计测试用例,编写测试代码,运行测试并观察...
在软件开发领域,Test-Driven Development(TDD)是一种编程实践,它强调先编写测试用例,然后编写满足这些测试的最小功能代码。Ruby作为一种动态、灵活的编程语言,是TDD的理想选择,因为它允许快速迭代和高效的...
《简单之美——软件开发实践者的思考》一书,深入剖析了这一艺术与哲学的内涵,向我们展示了如何在纷繁复杂的IT世界中,寻找到简洁高效的软件开发之道。本书不仅为初入行业的新人提供了实用的指南,也为经验丰富的...
本手册还提供了丰富的案例分析和实践指导,旨在帮助读者更好地理解和掌握UMTS-TDD仿真技术。例如,通过具体的场景设置,展示了如何在NS2环境中配置不同的网络参数、设置仿真场景以及分析仿真结果。此外,还包括了...
8. **最佳实践**:最后,本书总结了一系列TDD的最佳实践,帮助开发者避免常见错误,提升代码质量和开发效率。 #### 结语 通过《嵌入式TDD》,读者不仅能够深入了解TDD的核心原理,还能学习到如何将其有效地应用于...
### 使用PHPUnit进行TDD驱动开发 #### 一、引言 测试驱动开发(TDD, Test-Driven Development)是一种软件开发方法论,它要求在编写实际功能代码之前先编写测试用例。通过这种方式,可以确保代码的质量,并且有助...
"ThoughtWorks文集II——敏捷实践的秘密"是该公司分享其在敏捷开发领域的深入理解和实践经验的集合,旨在帮助读者深入了解如何有效地实施敏捷方法并从中获益。 在敏捷开发的世界里,核心理念是迭代和增量式开发,...
在C#的TDD实践中,还有一种称为"行为驱动开发"(Behavior Driven Development,BDD)的方法,它使用自然语言描述测试场景,使非技术人员也能理解。例如,我们可以使用SpecFlow这个工具,将Gherkin语法的测试场景转化...
《软件工程——实践者的研究方法》是软件工程领域的一部权威著作,由知名专家梅宏翻译,以Word格式呈现。这本书深入浅出地探讨了软件开发的理论与实践,为软件工程师提供了宝贵的指导。标签“软件工程”明确了本书的...
在软件开发领域,TDD(Test-Driven Development,测试驱动开发)是一种编程实践,它强调先编写测试用例,再编写实现功能的代码。Laravel,作为一款流行的PHP框架,高度支持TDD,使得开发者能够更高效、更稳定地构建...