|
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
|---|---|
| 作者 | 正文 |
|
时间:2006-11-16
我想大家使用easyMock的目的都是想去隔离层于层的依赖关系,防止由于被依赖层的错误导致依赖层的测试失败。 但是现在使用了EasyMock后发现了一个问题, 由于层之间的依赖而导致测试失败的情况是给隔离了。但是现在带来一个比较头痛的问题, 就是我必须去关注被测试对象的内部逻辑了,那这样的测试并不能叫做所谓了暗盒测试了, 请看下面的例子:
public class MaintainRoleService extends ServiceSupport implements IMaintainRoleService{
private IRoleDAO roleDAO;
public void setRoleDAO(IRoleDAO roleDAO) {
this.roleDAO = roleDAO;
}
public void saveRole(RoleVO roleVO) throws BusinessException {
if( roleVO == null || StringUtils.isBlank(roleVO.getId()) ){
throw new BusinessException(new Message("Arguments error"));
}
RoleVO returnedRoleVO = roleDAO.getByPk(roleVO.getId());
if( null != returnedRoleVO ){
throw new BusinessException( new Message("Inputed role number has existed in DB"));
}
if( null != roleVO.getParentRole() && !StringUtils.isBlank(roleVO.getParentRole().getId()) ){
RoleVO returnedParentRole = roleDAO.getByPk(roleVO.getParentRole().getId());
if ( returnedParentRole == null ){
throw new BusinessException( new Message("Unexpected error") );
}
if( !RoleVO.ROLE_ST_VALID.equalsIgnoreCase(returnedParentRole.getStCd()) ){
throw new BusinessException( new Message( "Cannot add a sub role with a inactived parent role" ) );
}
roleVO.setParentRole(returnedParentRole);
}
roleVO.setCreateDttm(DateUtil.getSystemTimestamp());
roleVO.setLastUpdDttm(DateUtil.getSystemTimestamp());
roleVO.setVersionNum(0);
roleDAO.add(roleVO);
}
}
上面是一个经常可以见到的一个insert操作,因为这里的service方法依赖了DAO,也就是上面的IRoleDAO, 所以我在写testCase的时候就需要用mock来模拟roleDAO. 但是在我的service方法内部可能会调用好多次roleDAO方法。 比如我上面的saveRole方法就可能调用两次或者三次roleDAO方法, 其实上面Service方法也就决定了我不可能先写测试类,再去写实现类使测试通过! 因为我前面的测试数据,主要也就是对mock的expect的设置需要去了解service方法里面的逻辑才能知道,到底被测试的方法调用了多少次, 并且分别是调用了哪些DAO方法,到底我应该怎么样去设置mock的expect... 所以我感觉我要是这样写unit test 是不成功,不知道有没有朋友知道什么好的方法,或者给我点好的建议呢! 声明:JavaEye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
|
|
| 返回顶楼 | |
|
时间:2006-11-16
引用 但是现在带来一个比较头痛的问题, 就是我必须去关注被测试对象的内部逻辑了,那这样的测试并不能叫做所谓了暗盒测试了 单元测试本来就是白盒测试啊! 当然你需要知道已知的输入参数下,应该会有如何的返回值,不然如何Mock? Mock只是让你不去关注方法的实现,但是并不代表你可以不关注这个方法的行为啊! |
|
| 返回顶楼 | |
|
时间:2006-11-16
Morgan0916 写道 引用 但是现在带来一个比较头痛的问题, 就是我必须去关注被测试对象的内部逻辑了,那这样的测试并不能叫做所谓了暗盒测试了 单元测试本来就是白盒测试啊! 当然你需要知道已知的输入参数下,应该会有如何的返回值,不然如何Mock? Mock只是让你不去关注方法的实现,但是并不代表你可以不关注这个方法的行为啊! 那也就是说, 如果用到了mock, 我也就没有办法去先写测试类再去写实现的具体类了?? 我的本意是测试类可以将被测试对象当作一个完全封闭的api来测试. 也就是从这个api的调用者去考虑! 本来是可以这样做的,不过现在加了mock后我就不可以这样做了...... 其实我们写的代码除了底层的 util 类, 有多少类是可以不用写mock直接测试的呀! 这样不就是永远也不可能达到从调用者去考虑写测试代码的境界了。 |
|
| 返回顶楼 | |
|
时间:2006-11-16
有没有朋友能帮我提供一个我上面代码中saveRole方法的测试呢!看看你们是怎么样去使用mock的呢!
|
|
| 返回顶楼 | |
|
时间:2006-11-16
你不知道调用多少次就不要在mock里边写明次数呗.那个次数是用来测试调用次数是否正确的.你还没有些实现,何必要测试那个依赖方法调用了多少次?可以等实现完毕后再补上这个测试用例.
|
|
| 返回顶楼 | |
|
时间:2006-11-16
我稍微写了一下测试类,请大家看看了! 不过我感觉这样的testCase很不稳定,太依赖被测试类了。如果被测试类做了些改动估计就要回头来改对应的testCase了。
public class MaintainRoleServiceTest extends TestCase {
private MaintainRoleService maintainRoleService;
private IRoleDAO roleDAOMock;
protected void setUp() throws Exception {
super.setUp();
maintainRoleService = new MaintainRoleService();
roleDAOMock = createMock(IRoleDAO.class);
maintainRoleService.setRoleDAO(roleDAOMock);
}
protected void tearDown() throws Exception {
super.tearDown(); //To change body of overridden methods use File | Settings | File Templates.
}
public void testSaveRole(){
//1. role have no parent role and passed all validation
RoleVO roleVO = new RoleVO();
roleVO.setId("ROLE_USER");// Role Number
roleVO.setRoleDesc("users"); // Role Description
roleVO.setStCd(RoleVO.ROLE_ST_VALID);
roleVO.setCreateUsrNum("DenisLing");
roleVO.setLastUpdUsrNum("DenisLing");
reset(roleDAOMock);
expect(roleDAOMock.getByPk("ROLE_USER")).andReturn(null);
expect(roleDAOMock.add(roleVO)).andReturn( "ROLE_USER");
replay(roleDAOMock);
try{
maintainRoleService.saveRole(roleVO);
}catch( BusinessException be ){
fail("Should not run here");
}
verify(roleDAOMock);
//2.role have no parent role but inputed role number has existed in db
reset(roleDAOMock);
expect(roleDAOMock.getByPk("ROLE_USER")).andReturn(roleVO);
replay(roleDAOMock);
try{
maintainRoleService.saveRole(roleVO);
fail("should not run here");//
}catch( BusinessException be ){
assertEquals("Inputed role number has existed in DB",be.getExceptionMessage().getMessageKey());
}
//3. test the case with parent Role but parent role does not existed in DB
RoleVO parentRoleVO = new RoleVO();
parentRoleVO.setId("ROLE_PARENT_USER");
roleVO.setParentRole(parentRoleVO);
reset(roleDAOMock);
expect( roleDAOMock.getByPk("ROLE_USER") ).andReturn(null); // no inputted role existed in DB
expect( roleDAOMock.getByPk("ROLE_PARENT_USER") ).andReturn(null);// parent role doesn't exsited in DB
replay(roleDAOMock);
try{
maintainRoleService.saveRole(roleVO);
fail();// should not run here
}catch( BusinessException be ){
assertEquals("Unexpected error",be.getExceptionMessage().getMessageKey());
}
verify(roleDAOMock);
//4. test the case with parent Role but parent role's ST_CD is "S" suspend or "T"
RoleVO returnedParentRoleVO = new RoleVO();
returnedParentRoleVO.setId("ROLE_PARENT_USER");
returnedParentRoleVO.setStCd("S");
reset(roleDAOMock);
expect(roleDAOMock.getByPk("ROLE_USER")).andReturn(null);
expect(roleDAOMock.getByPk("ROLE_PARENT_USER")).andReturn(returnedParentRoleVO);
replay(roleDAOMock);
try{
maintainRoleService.saveRole(roleVO);
fail();
}catch(BusinessException be){
assertEquals("Cannot add a sub role with a inactived parent role",be.getExceptionMessage().getMessageKey());
}
verify(roleDAOMock);
returnedParentRoleVO.setStCd("T");
reset(roleDAOMock);
expect(roleDAOMock.getByPk("ROLE_USER")).andReturn(null);
expect(roleDAOMock.getByPk("ROLE_PARENT_USER")).andReturn(returnedParentRoleVO);
replay(roleDAOMock);
try{
maintainRoleService.saveRole(roleVO);
fail();
}catch(BusinessException be){
assertEquals("Cannot add a sub role with a inactived parent role",be.getExceptionMessage().getMessageKey());
}
verify(roleDAOMock);
}
}
|
|
| 返回顶楼 | |
|
时间:2006-11-16
这个test方法也太长了吧
|
|
| 返回顶楼 | |
|
时间:2006-11-17
lingcm 写道 Morgan0916 写道 引用 但是现在带来一个比较头痛的问题, 就是我必须去关注被测试对象的内部逻辑了,那这样的测试并不能叫做所谓了暗盒测试了 单元测试本来就是白盒测试啊! 当然你需要知道已知的输入参数下,应该会有如何的返回值,不然如何Mock? Mock只是让你不去关注方法的实现,但是并不代表你可以不关注这个方法的行为啊! 那也就是说, 如果用到了mock, 我也就没有办法去先写测试类再去写实现的具体类了?? 我的本意是测试类可以将被测试对象当作一个完全封闭的api来测试. 也就是从这个api的调用者去考虑! 本来是可以这样做的,不过现在加了mock后我就不可以这样做了...... 其实我们写的代码除了底层的 util 类, 有多少类是可以不用写mock直接测试的呀! 这样不就是永远也不可能达到从调用者去考虑写测试代码的境界了。 现在就拿你的这个例子来说吧.比如你想测试MaintainRoleService,而MaintainRoleService又依赖IRoleDAO object,那么你就需要对IRoleDAO做Mock.但是你必须先要写一个IRoleDAO interface,你并不需要去写实现类!但是你必须要在Mock中设定IRoleDAO interface中每个method的正确的行为,已知的输入参数下,应该会有如何的返回值,不然如何断言? 比如你要Mock roleDAO.add(roleVO), 再add()的实现中,或许是许多的数据库操作,但是我们此时mock的时候不用去关心这些,我们要关心的是调用这个方法后,如果添加成功,返回什么? 添加失败,又返回什么?此时不就可以去mock它可能的这些情况吗? 你不用去看别人的实现代码,看看设计文档就可以知道啦 |
|
| 返回顶楼 | |
|
时间:2006-11-17
参考Martin Fowler的这篇文章:
http://www.martinfowler.com/articles/mocksArentStubs.html 区别就在于测试是基于状态的还是基于交互的。个人比较喜欢基于状态的,使用Mock也不喜欢做verify,拿来当stub用。 |
|
| 返回顶楼 | |
|
时间:2007-01-08
你的感觉是对的,mock的方式其实就是在测试类的实现过程,已经不仅仅是在测试接口了。
对于一个代码来说,如果你测试他的时候感觉非mock不可,不然就没什么可测的,其实他的设计就已经出问题了,一定是各个功能模块的耦合性太高了。 mock和stub的区别是很大的,简单的说:mock是在测试一个类的内部是否正确;stub是假设这个类的接口完全正常,用它去测试其他的类。 |
|
| 返回顶楼 | |











