junit--用mock object进行隔离测试(二)

原创 2005年03月01日 15:44:00

第三节(作为一个重构技术来使用mockobject Using mock objects as a refactoring technique

   很多人习惯上认为单元测试应该完全透明而且不应该改变runtime code 来简化测试。这个观点是错误的。

   单元测试时runtime code 的一级用户而且应该和其他的用户受到同样的对待。如果你的代码对测试的使用来说没有弹性,那么你应该修改你的代码.

   据个例子,你怎样看待下面的代码?

[...]

import java.util.PropertyResourceBundle;

import java.util.ResourceBundle;

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

[...]

public class DefaultAccountManager implements AccountManager

{

private static final Log LOGGER =

LogFactory.getLog(AccountManager.class);

//注释一

public Account findAccountForUser(String userId)

{

LOGGER.debug("Getting account for user [" + userId + "]");

ResourceBundle bundle =

PropertyResourceBundle.getBundle("technical");

//注释二

String sql = bundle.getString("FIND_ACCOUNT_FOR_USER");

// Some code logic to load a user account using JDBC

[...]

}

[...]

}

注释一:你获得一个由LogFactory产生的log对象

注释二:使用PropertyResourceBundle来获得一个sql命令。

你认为这个代码好吗?我们来看研究两点,他们都与代码的扩展性和灵活性有关。第一个问题是:不可能来采用不同的Log对象,因为它是在类里面产生的。比如,在测试的时候,你或许想要使用一个Logdo nothing,但是你不能。

   一般的规则是:一个像这样的类应该能够使用无论什么样的Log对象。这个类的目的是为了展现JDBC logic而不是来记日至。

  同样的论述也适用于PropertyResourceBundle.他看起来现在很正常,但是当你决定采用xml来存贮配置的时候会发生什么呢?同样,这个类不是用来处理使用什么样实现的。

   一个有效的设计策略是移植一个对象到其他对象时不做改变。外围对象的选择应该在调用的更高曾控制。基本上,当你转移到调用曾,决定用logger或者配置

应该被推到上一层。这种策略提供了代码的扩展型而且能够处理变化。变化是永恒的

   1,简单重构(Easy refactoring):

为了是域对象能够传播而重构所有的代码可能就是浪费时间。为了一个单元测试,你可能不准备重构你的代码

。幸运的是,有一个简单的重构技术可以让你保留你原来的接口但却允许他在域对象(或许并没有产生)中传播。做为证明,让我们来看看如何重构DefaultAccountManager这个类。

public class DefaultAccountManager implements AccountManager

{

private Log logger;

private Configuration configuration;

//注释1

public DefaultAccountManager()

{

this(LogFactory.getLog(DefaultAccountManager.class),

new DefaultConfiguration("technical"));

}

public DefaultAccountManager(Log logger,

Configuration configuration)

{

this.logger = logger;

this.configuration = configuration;

}

public Account findAccountForUser(String userId)

{

this.logger.debug("Getting account for user ["

+ userId + "]");

this.configuration.getSQL("FIND_ACCOUNT_FOR_USER");

// Some code logic to load a user account using JDBC

[...]

}

[...]

}

注意注释1,你把PropertyResourceBundle类交换为一个新的Configuration接口。这使得代码更有弹性因为他引入了一个接口(很容易模拟),而且Configuration的实现可以很灵活的实现(包括使用resource bundles)。现在的设计更好了因为你可以重复使用DefaultAccountManager类,在其中可以任意实现LogConfiguration

接口(如果你使用构造函数,他带有两个参数)。这个类能在外部调用。同时,你没有破坏已经存在的接口,因为你只不过添加了一个构造函数。你保留了原始的默认构造函数----仍然初始化loggerconfiguration两个成员变量通过默认的实现(implementations)。

   通过这个重构,你已经提供了一个trapdoor为了控制来自测试中的域对象,你保留了向后兼容性而且铺垫了通向未来的很容易的重构之路。调用类可以按自己的方式使用新的构造函数。

2   更具扩展性的代码(Allowing more flexible code

   在上一节中我们介绍了一个很有名的模式---控制反转(IOC),主要意思是具体化来自外部类/方法的所有

的域对象,而且传递所有的东西给他。不是由类来实例化对象,而是实例传递到类上(通常通过接口)。

   在例子中,他的意思是 传递LogConfiguration对象到DefaultAccountManager类上,DefaultAccountManager没有限制什么样的实例可以传进来或者他们怎样被构造的。他只知道他们实现了LogConfiguration接口。

     IOC----控制反转的定义:<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

    应用ioc 模式于类中意思是去掉对一个类的初始化--当这个类没有立即被需要--而且代替的是传递必要的实例。这些实例可能会被传递通过一种特殊的构造方法,通过一个setter或者作为一个方法的参数。调用代码来正确的设置这些被调用的类域对象成为了一种职责。

  下面我们来看看为findAccountByUser方法写一个测试将会使如何的简单。

public void testFindAccountByUser()

{

MockLog logger = new MockLog();

//注释一

MockConfiguration configuration = new MockConfiguration();

configuration.setSQL("SELECT * [...]");

//注释2

DefaultAccountManager am =new DefaultAccountManager(logger, configuration);

//注释3

Account account = am.findAccountForUser("1234");

// Perform asserts here

[...]

}

}

注释1 使用了一个实现了Log接口但是没有做任何事情的模拟logger

注释2 产生了一个MockConfiguration 实例并且返回一个给定的sql查询--当调用Configuration.getSQL的时候。

注释3 产生了一DefaultAccountManager实例,你可以测试,传给他logconfiguration实例

   你已经能够完全控制你的loggingconfiguration(来自外部代码)来进行测试,在代码中。因此,你的代码是扩展性良好而且比较灵活的(允许任何loggingconfigutation的实现在此应用)你将看到更多的这些代码的重构在这一章节和下面的章节中。

   最后要值得注意的是,如果你先写你的测试代码,你将会自动设计你的代码具有扩展性。扩展性是一个关键

当你写单元测试的时候。如果你测试在先,你将不会招致需要以后为了扩展性而重构你的代码。

junit--用mock object进行隔离测试(上)

最近抽空翻译了一下manning出版的junit in action,译的比候捷还糟,大家不要bt.以下是第7章的内容第一节   mock object 的介绍:      隔离测试有很多好处,比如测...

如何通过测试替代(Test Doubles)合理隔离单元测试以提高单元测试效率

如何通过测试替代(Test Doubles)合理隔离单元测试以提高单元测试效率 测试技术在现代软件工程中变得愈来愈重要,单元测试是软件质量的第一关。开发者通过单元测试可以快速反思自己的设计,提...
  • cxboyee
  • cxboyee
  • 2016年01月14日 12:27
  • 769

比较完整的junit单元测试之-----mock模拟测试

介绍   本文将介绍模拟测试框架Mockito的一些基础概念, 介绍该框架的优点,讲解应用Mockito的Java示例。 模拟(Mock)的概念    在软件开发的世界之外, "...

JUnit学习笔记10---mock object进行孤立测试4

尝试2:使用类工厂进行重构       我们采用Inversion of Control(IoC)模式,这个模式要求用到的任何的资源都要传给getContent方法或WebClient类。现在我...

使用junit+mockito进行mock测试实例

刚开始做网站的时候,测试都是这样的(比如测试修改用户信息的表单逻辑):设置断点,启动运行程序,登陆,点击点击点击然后进入到测试的页面,输入测试数据,进入断点…….。一个大的程序启动又慢,还要登陆,还要...

Jmock和Junit4结合进行mock测试

jMock 2 Cheat Sheet Test Fixture Class Raw import org.jmock.Mockery; import org.jmock.Expect...

JUnit学习笔记7---mock objects进行孤立测试1

The secret of success is sincerity.once you can fake that  you have you have got it made.         ——...

使用模拟对象(Mock Object)技术进行测试驱动开发

方 世明 (fangshim@cn.ibm.com), 软件工程师, EMC 方世明就职于 IBM 中国软件开发中心存储部门,从事存储设备管理软件的开发工作。 简介: 测试...

Java单元测试(Junit+Mock+代码覆盖率)

单元测试是编写测试代码,用来检测特定的、明确的、细颗粒的功能。单元测试并不一定保证程序功能是正确的,更不保证整体业务是准备的。单元测试不仅仅用来保证当前代码的正确性,更重要的是用来保证代码修复、改进或...
  • web718
  • web718
  • 2015年05月15日 09:40
  • 483

Java单元测试(Junit+Mock+代码覆盖率)

单元测试是编写测试代码,用来检测特定的、明确的、细颗粒的功能。单元测试并不一定保证程序功能是正确的,更不保证整体业务是准备的。 单元测试不仅仅用来保证当前代码的正确性,更重要的是用来保证代码修复...
  • paul342
  • paul342
  • 2016年06月13日 14:09
  • 892
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:junit--用mock object进行隔离测试(二)
举报原因:
原因补充:

(最多只允许输入30个字)