测试的粘结度
最近一直在写操作符处理的单元测试。正如liangfei所说,想要更好的优化表达式,首先得十分了解操作符的功能,而写单元测试就是非常好的一个途径。十分赞同这个观点,所以我最近一直在写测试,也确确实实地了解了操作符的功能。
在测试类中如何获得操作符对象呢?我参考了一下写完的测试类,发现是 new 出来的。可是在程序中,操作符不是new出来的,而是通过一个IOC容器获得的,而且获得是某一种操作符的handler,例如下面这样:
Configuration config = PropertiesConfigurationLoader.loadStandardConfiguration();
// 默认会取得 StandardOperatorHandlerProvider
operatorHandlerProvider = config.getOperatorHandlerProvider();
handler = operatorHandlerProvider.getUnaryOperatorHandler(".");
这个handler就是处理"."符号的了,通过这个handler,再去调用"."号操作符。这么做的目的我想是为了实现操作符的重载。
所以,在CommonTemplate里,是不可能直接得到操作符对象的。因此,在测试类中,我也就使用了这种方式,来获得操作符进行测试。
不过liangfei并不同意这种方式。他认为测试操作符的时候,应该在测试类中 new 出操作符,然后对操作符进行测试。理由是这样可以把要测试的目标功能分隔开来。我的理解就是降低测试的粘结度。但是这样就产生另外一个问题,如何保证 OperatorHandler的正确性?
或许应该再编写测试类,从 OperatorHandler 开始,测试到每一个操作符。这样确实很麻烦。但是如果不做这种测试的话,又不能保证 OperatorHandler 的正确性。
那么测试的粘合度如何控制比较好?例如一个项目,有业务层,和持久层。那么当对这两个层写测试的时候,改如何进行?
按照所理解的TDD的方式,在实现持久层的时候,必然会写持久层的测试。在写业务层的时候,再去写业务层的测试,这时,业务层的测试也间接包含了持久层的内容。那我们是不是可以跳过持久层的测试?当然不可以。
所以问题又回来了,对于CommonTemplate来说,我们是不是可以跳过操作符的测试?当然不可以--事实上我们也确实没有跳过,现在写的就是操作符的测试。那么我们可以不可以跳过 OperatorHandler 的测试?也是不可以。不过,OperatorHandler 中逻辑很少,只是按照责任链的模式把变量分配到正确的操作符上。
因此,我们是不是可以把 OperatorHandler 的测试和操作符的测试一起做了?虽然这样做测试粘合度比较大,但是 OperatorHandler 的逻辑几乎没有,如果再单独对它进行测试,就和测试操作符完全重复了。
所以,是不是也可以这样考虑,两个类,虽然有关联,但是其中一个类没有逻辑,或者逻辑非常少,那么可以不考虑粘合度,而直接对这两个类一起进行测试?
这个问题我不知道答案。不过我还是遵从liangfei的意见,按照降低粘合度的方式修改了测试类。希望能在以后的积累中,得到一个满意的答案。
- 20:21
- 浏览 (1378)
- 评论 (1)
- 分类: CommonTemplate
- 相关推荐
评论
(1) 隔离:每次只测试一个类,并Mock其所依赖的类,这样才能准确定位问题所在,并且保证没试用例的职责分明。
(2) 无序:每一个测试用例都不应该依赖于其它测试用例。
(3) 自动:验证过程可自动完成,不需要人为查看输出等。
(4) 可重复:测试用例应可重复执行,如果修改了状态,应在tearDown中恢复。
如果实在需要测试多操作符集成, 也可以自行组装, 而不依赖配置容器:
BinaryOperatorHandlerChain chain; setUp: List handlers = new ArrayList(); handlers.add(new ObjectFunctionOperatorHandler()); handlers.add(new ObjectPropertyOperatorHandler()); handlers.add(new ScopesGetterOperatorHandler()); handlers.add(new MapGetterOperatorHandler()); chain = new BinaryOperatorHandlerChain(); chain.setRightOperandNamed(true); // 点号的右参不作为变量 chain.setRightOperandFunctioned(true); chain.setBinaryOperatorHandlers(handlers); // 然后测试chain
另外, 你说的项目中的测试也应隔离, 下面给出的是简单的测试例子:
package com.xxx.action;
import junit.framework.TestCase;
import com.xxx.action.mock.EntryBizMock;
/**
* 入口模块Action测试
*
* @author 梁飞
*
*/
public class EntryActionTestCase extends TestCase {
// 待测试Action类 (Struts2)
private EntryAction entryAction;
// Biz的仿造类
private EntryBizMock entryBizMock;
@Override
protected void setUp() throws Exception {
entryBizMock = new EntryBizMock();
entryAction = new EntryAction();
entryAction.setEntryBiz(entryBizMock);
}
@Override
protected void tearDown() throws Exception {
entryBizMock = null;
entryAction = null;
}
/**
* 测试登录
*
* @throws Exception
*/
public void testLogin() throws Exception {
// 注入测试参数
entryAction.setUsername("liangfei");
entryAction.setPassword("123456");
// 执行被测试方法
String result = entryAction.login();
// 断言返回值是否正确
super.assertEquals(EntryAction.SUCCESS, result);
// 断言Biz是否被调用
super.assertTrue(entryBizMock.isLogin());
// 断言Biz是否收到正确数据
super.assertEquals("liangfei", entryBizMock.getUsername());
super.assertEquals("123456", entryBizMock.getPassword());
}
/**
* 测试退出登录
*
* @throws Exception
*/
public void testLogout() throws Exception {
// 执行被测试方法
String result = entryAction.logout();
// 断言返回值是否正确
super.assertEquals(EntryAction.SUCCESS, result);
// 断言Biz是否被调用
super.assertTrue(entryBizMock.isLogout());
}
}
package com.xxx.action.mock;
import com.xxx.biz.IEntryBiz;
import com.xxx.exception.LoginException;
/**
* 入口业务的仿造实现类
*
* @author 梁飞
*
*/
public class EntryBizMock implements IEntryBiz {
// ---- login mock ----
@Override
public void login(String username, String password) throws LoginException {
this.login = true;
this.username = username;
this.password = password;
}
private String username;
/**
* login()所接收到的用户名
*
* @return 用户名
*/
public String getUsername() {
return username;
}
private String password;
/**
* login()所接收到的密码
*
* @return 密码
*/
public String getPassword() {
return password;
}
private boolean login = false;
/**
* login()是否被调用标识
*
* @return 调用标识
*/
public boolean isLogin() {
return login;
}
// ---- logout mock ----
@Override
public void logout() {
this.logout = true;
}
private boolean logout = false;
/**
* logout()是否被调用标识
*
* @return 调用标识
*/
public boolean isLogout() {
return logout;
}
}
当然, 如果觉得Mock很烦, 可以用EasyMock等其它工具简化.
发表评论
- 浏览: 135180 次

- 详细资料
搜索本博客
我的相册
共 3 张
最新评论
-
TDD,想说爱你不容易
stevenwang 写道这个论题我喜欢。 早想写一点文字,来纪念我TDD的失 ...
-- by kozyan -
测试驱动?很傻很天真
同情,很傻很天真
-- by passyt -
BNF范式
确实很有兴趣,刚刚学习完状态机的部分,对比一下CT中状态机的实现,很有收获
-- by yananay -
BNF范式
看来你对编译知识很有兴趣, 希望你能有所突破. BNF本身并不复杂, 只是表达 ...
-- by javatar -
CT中表达式处理的思考
你考虑的很对, 当时设计时, 我也考虑过这个问题, 在编译原理中, 通常都会把" ...
-- by javatar






评论排行榜