第五次重构我们引入了数据库的设计,用户信息要从数据库中读取,问候语库存储在数据库中,并支持添加与更新。数据库的引入使自动化测试变得困难了,因为数据状态总是变化着的,而这种变化使得测试过程不能复现,这是我们不愿看到的。因此,我们在设计时将业务与数据库访问分离,形成了UserDao与GreetingRuleDao。此时,我们的设计应当遵从“依赖反转”原则,即将UserDao与GreetingRuleDao设计成接口,并编写它们的实现UserDaoImpl与GreetingRuleDaoImpl。这样设计就为我们Mock掉UserDao与GreetingRuleDao的实现类创造了条件。
这是我们的设计:
图4.3 HelloWorld的设计图
为此,我们编写了这样的测试程序:
private HelloWorld helloWorld = null;
private GreetingToUserImpl greetingToUser = null;
private GreetingAboutTimeImpl greetingAboutTime = null;
private final static List<GreetingRule> GREETING_RULES = getRules();
/**
* @throws java.lang.Exception
*/
@Before
public void setUp() throws Exception {
helloWorld = new HelloWorld();
greetingToUser = new GreetingToUserImpl();
greetingAboutTime = new GreetingAboutTimeImpl();
helloWorld.setGreetingToUser(greetingToUser);
helloWorld.setGreetingAboutTime(greetingAboutTime);
}
/**
* @throws java.lang.Exception
*/
@After
public void tearDown() throws Exception {
helloWorld = null;
greetingToUser = null;
greetingAboutTime = null;
}
/**
* Test method for {@link org...HelloWorld#sayHello(java.util.Date, java.lang.String)}.
*/
@Test
public void testSayHelloInTheMorning() {
final Date now = DateUtil.createDate(2013, 9, 7, 9, 23, 11);
final long userId = 2013090701;
UserDao userDao = createMock(UserDao.class);
GreetingRuleDao greetingRuleDao = createMock(GreetingRuleDao.class);
expect(userDao.loadUser(userId)).andAnswer(new IAnswer<User>(){
@Override
public User answer() throws Throwable {
User user = new User();
user.setUserId(userId);
user.setName("鲍晓妹");
return user;
}});
expect(greetingRuleDao.findAllGreetingRules())
.andAnswer(new IAnswer<List<GreetingRule>>(){
@Override
public List<GreetingRule> answer() throws Throwable {
return GREETING_RULES;
}});
replay(userDao);
replay(greetingRuleDao);
greetingToUser.setUserDao(userDao);
greetingAboutTime.setGreetingRuleDao(greetingRuleDao);
String result = helloWorld.sayHello(now, userId);
Assert.assertEquals("Hi, 鲍晓妹. Good morning!", result);
verify(userDao);
verify(greetingRuleDao);
}
这段测试程序比较长,但其它部分都是打酱油的,核心是那个testSayHelloInTheMorning()用例,即问候早上好这个用例。userDao与greetingRuleDao是两个接口,我们在实例化它们的时候,并没有去创建它们的实现类,而是用Mock的方式进行创建:
UserDao userDao = createMock(UserDao.class);
GreetingRuleDao greetingRuleDao = createMock(GreetingRuleDao.class);
随后我们开始定义它们的行为loadUser()与getAllGreetingRules()。在这个测试用例中,我们并不关心它们是怎样去数据库里查询数据并返回的,我们只关心它们是否得到应该得到的参数,并要求它们按照规定返回一个结果:
final long userId = 2013090701;
expect(userDao.loadUser(userId)).andAnswer(new IAnswer<User>(){
@Override
public User answer() throws Throwable {
User user = new User();
user.setUserId(userId);
user.setName("鲍晓妹");
return user;
}});
我们希望被测程序在执行的时候调用了userDao.loadUser(userId),并且调用时传入的参数userId = 2013090701。如果测试过程中传入的参数是这个值,这一项检查点可以通过,否则不能通过。随后我们希望该函数返回“鲍晓妹”这个用户对象。通过Mock程序,我们完全将数据库访问的过程剥离在自动化测试之外,而只是验证它的输入参数,并指定测试所需的返回结果。也就是说数据访问过程被Mock掉,而大大降低了测试难度。
如果UserDao与GreetingRuleDao的Mock程序不能得到规定的参数时,测试就不能通过,这就是说传递给Mock程序的参数也成为了测试程序要验证的一个输出。随后,Mock程序返回规定值,该规定值则成为了被测程序的一个输入。最后,被测程序根据这个输入返回结果,为测试程序所验证,测试结束(如图4.4所示)。
图4.4 HelloWorld的自动化测试
图中的BUS层才是我们大量编码,应当自动化测试的部分。既然是测试就是验证怎样的输入,应当得到怎样的输出。Web层向BUS层发出的请求,即调用BUS层某个类的方法,就是测试用例中的一个输入,执行完该方法后的返回值就是测试用例的一个输出。但是,这对输入输出并不是该测试用例的全部。这里经过BUS层处理以后,经过一系列的逻辑判断和数据操作,随后会去调用DAO层进行数据访问操作。调用DAO层时所传递的参数,就是测试用例的另一个输出。图中,从DAO层的输入,到它的输出,这段数据库访问的过程被Mock程序Mock掉了,因为它不在这个用例的测试范围。然后DAO层返回给BUS层一个结果,该结果就是测试用例的另一个输入。接着BUS层会再次对这个返回结果进行处理,最后返回给Web层最终的结果。这就是采用Mock方式进行自动化测试的一个完整流程。
采用自动化测试,测试程序将不再验证后台的数据库是否正确,同时也不再验证前台的Web应用及其前端设备是否正确。在该例中,系统的真正目的是要在前台显示对用户的问候,因此将会有一个Action或Servlet调用HelloWorld:
Date now = DateUtil.getNow();
String user = SessionUtil.getCurrentUser(session);
HelloWorld helloWorld = new HelloWorld();
String greeting = helloWorld.sayHello(now, user);
request.setAttribute(“greeting”, greeting);
然而,这些程序都不适合自动化测试而应采用手工测试。回顾HelloWorld自动化测试建立的过程我们不难发现,它在设计之初就实现了业务逻辑与Web应用、与数据访问的分离,所以它可以轻易的建立自动化测试。但是,不幸的是,我们大多数的遗留系统在设计之初都没有考虑到这些。因此,我说,在重构之初首先建立自动化测试机制是不现实的,我们只能采用手工测试结合QTP的方式。只有当我们通过重构,使系统架构满足自动化测试的条件之后,自动化测试才可以开展。
毫无疑问,测试与重构形成了一个“鸡生蛋,还是蛋生鸡”的怪圈,成为我们实践系统重构一大拦路虎。本书将在后面详细讨论这个话题(详见 第十六章 测试的困境),为你破解这个谜团。
大话重构连载首页:
http://fangang.iteye.com/blog/2081995
特别说明:希望网友们在转载本文时,应当注明作者或出处,以示对作者的尊重,谢谢!
- 大小: 39.2 KB
- 大小: 10.9 KB
分享到:
相关推荐
Google Mock 是一个强大的 C++ 单元测试框架,它允许开发者创建模拟对象(Mock Objects)以测试复杂的系统中各个组件的交互。Mock 对象能够模拟真实对象的行为,并且可以预设它们在特定调用下的响应,这对于隔离测试...
单元测试:单元测试案例:Mock对象在单元测试中的应用.docx
Python 提供了一个强大的库——`unittest.mock`,用于模拟(mock)对象、方法和类,以便在测试中隔离依赖关系,专注于测试目标代码的功能。本练习主要关注如何使用 `unittest.mock` 进行mock测试数据。 一、Mock...
在Python的单元测试中,Mock是一个非常重要的工具,它允许我们模拟对象的行为,以便在测试过程中隔离被测试代码与其他系统的交互。Mock可以帮助我们避免在测试时产生不必要的副作用,提高测试速度和效率。以下是对...
### 使用Mock对象在Java单元测试中的重要性及实践 #### 引言 在软件开发过程中,单元测试作为确保代码质量的重要环节,其有效性往往受到多种因素的影响,尤其是当待测方法依赖于外部资源如网络、数据库或其他复杂...
Mock技术在软件开发中扮演着重要的角色,尤其是在测试领域。它允许开发者在不依赖实际环境或第三方服务的情况下,创建模拟对象来代替真实组件,以便进行单元测试和集成测试。这样可以提高测试效率,减少测试复杂性,...
模拟一切使用 mock 进行 python 单元测试的示例模拟 python 2.7: : (这里也有很多很好的例子) 从图书馆的主页: “mock 是一个用于在 Python 中进行测试的库。它允许您用模拟对象替换被测系统的某些部分,并对...
Mock 测试技术详解与 Fiddler AutoResponder 面板介绍 Mock 测试是一种软件测试方法,旨在模拟一些难以构造或获取的对象,以便进行测试。在测试过程中,Mock 对象可以帮助开发者更好地测试单元测试、集成测试和系统...
3. **接口测试**:对于多模块协作的项目,MOCK挡板软件能够模拟不同模块间的接口通信,帮助测试单个模块的功能,同时减少因其他模块未完成导致的测试难题。 4. **灵活性与可配置性**:用户可以根据需求自定义挡板...
服务端Mock测试是一种在软件开发中广泛应用的技术,它允许测试工程师在没有依赖服务端的真实环境或者服务尚未完成时进行测试工作。Mock技术通过模拟真实的服务端行为,使得测试人员能够在本地模拟各种网络交互,从而...
在IT行业中,Mock技术是一种非常重要的测试方法,特别是在前端开发中。它允许开发者在不依赖实际后端服务的情况下,对应用程序进行单元测试和集成测试。`模拟mock.zip`这个压缩包文件显然与Mock技术相关,其中包含的...
本文将详细探讨如何利用Mock和依赖注入(Dependency Injection, 简称DI)技术来测试Objective-C中的网络功能。TestNetworking项目正是这样的一个实践案例。 首先,Mock技术是单元测试中常用的一种方法,它允许我们...
Mock方法则是单元测试中的一个重要工具,用于模拟复杂系统中的依赖关系,以便孤立地测试目标代码。在本篇文章中,我们将深入探讨单元测试与Mock方法的相关知识点。 首先,让我们理解单元测试的基本概念。单元测试是...
在软件开发中,尤其是在测试阶段,MockServer可以替代真实的后端服务,允许开发者在没有实际依赖的情况下进行单元测试和集成测试。这个“mockserver1.zip”文件包含了启动和使用MockServer所需的一些基本组件和配置...
4. **重构辅助**:测试是重构过程中的安全保障,Google Test可以帮助确保重构后代码的功能完整性。 总的来说,Google C++测试框架和Google Mock是C++开发者的重要工具,它们提升了测试的质量和效率,降低了软件的...
Mock Object是一种在单元测试中模拟真实对象的行为和交互的技术,它允许测试人员控制对象间的依赖关系,从而在隔离环境中测试单个组件。这样可以避免复杂的真实系统依赖,提高测试的效率和准确性。 首先,Eclipse是...
本文详细介绍了如何使用Mock技术来进行Struts1框架的单元测试。通过引入StrutsTest框架,并结合具体的示例代码,展示了如何在不启动Web容器的情况下模拟HTTP请求响应过程,从而更高效地完成单元测试工作。这对于提高...
总结来说,本文档提供的内容涵盖了单元测试的基本概念、Mock技术的运用、JUnit框架的详细讲解,以及Mockito、MockMVC和Mock.js等工具的使用。通过这些知识,开发者可以构建更健壮的测试体系,提升代码质量和软件工程...
PostMan是一款强大的API测试工具,它也提供了Mock Server的功能,使得前端开发者可以在后端接口尚未完成的情况下进行开发和测试。本文将详细介绍如何使用PostMan实现Mock功能,从基础操作到高级应用,帮助你从入门到...