论坛首页 Java企业应用论坛

俺摸,俺摸,俺默默摸

浏览 11727 次
该帖已经被评为精华帖
作者 正文
   发表时间:2008-01-16  
OO
猪肉炖粉:“那啥,猪哥啊,这两天你身价大涨,发了吧?忙啥呢?”
一只猪:“可不是!到哪儿都被采访,这名猪也有隐私权滴!最近特想过普通猪的生活。这不,洗尽铅华,老老实实地躲圈里整Java呢”

话说这Java的一级摸客(EasyMock),老猪最近用的满多的。在Java 4里面不忍卒睹的代码在java 5里面骤然变得性感许多。不过用的多了,也发现有些不方便的地方。最不爽的就是:俺老猪不是粗心吗?有时候不小心就会忘了EasyMock.verify()或者IMocksControl#verify()。

这一忘了不打紧,有些bug就藏在那,测试也过了,但是bug也没找出来。
有很多淫说这还不容易?用IMocksControl,然后在tearDown()里写control.verify(),就没事了。(不好,被你发现老猪还在用回立牌JUnit 3.8)

嘿嘿,老猪针对这种左倾机会主义思潮做出了最坚决的斗争。

为啥?且听老猪我慢慢到来。在tearDown()里面写verify()有三个很致命的毛病:
1。 JUnit 3.8的tearDown()相当于try-finally里面的finally。也就是说,即使你的test出现了异常,它也会被执行。因此如果你的测试代码还没等到verify()的时候就因为某种原因歇菜了(比如,某个assertEquals()失败了),这个异常会被吞噬掉,你得到的将会是一个毫无意义的EasyMock的verify()异常。

2。tearDown()很有可能被子类重载滴。万一子类忘记了调用super.tearDown()怎么办?”子类不应该忘“?呵呵,要我说你还不如干脆就”不应该“忘记调用verify()呢。这不是前门驱虎,后门进狼么?

3。不能不分3*7=21就verify呀同志!咱假如说,俺老猪懒,为了跑到绿草如茵的山坡睡个暖洋洋的懒觉,写了一个啥也不干的testStone(),反正回头就说这山叫石头山,山上有个石头洞,洞里有俩和尚在讲故事...(说走嘴了),迷迷糊糊闭着眼睛哼着小曲点击了一下my precious testStone()。“啦啦啦啦啦,啦啦啦啦,天空出彩霞呀,.哦?.哦.bug出来啦呀 ”。啥?居妍说俺的testEmpty错了?俺老猪是无辜地呀。因为俺啥也没干啊!是猴哥。没错,肯定是这弼马温!说啥不能还没replay()呢就verify()?废话,我也没让你verify呀。这个故事告诉我们,不能没事就verify(),你至少得知道人家replay()了没有先。


老猪的解决办法是做一个PorkMockTest类,然后在TestCase#runTest()上做文章:
public class PorkMockTest extends TestCase {
  private IMocksControl control = null;
  private boolean replayed = false;
  private boolean verified = false;

  @Override protected void runTest() throws Throwable {
    super.runTest();
    if (replayed && !verified) {
      verify();
    }
  }
  private IMocksControl control() {
    if (control == null) {
      control = EasyMock.createMockControl();
    }
  }

  public <T> T mock(Class<T> type) {
    return control.createMock(type);
  }

  protected void replay() {
    control().replay();
    replayed = true;
  }

  protected void verify() {
    verified = true;
    control().verify();
  }
}



这个代码还比较简陋,还不处理strict和nice mock。不过呢,基本上就剩照猫画虎了——当然,我没跟你说就copy&paste啊,你怎么也得refactor一下才好意思见人吧?

好了,现在只要你继承我这个PorkMockTest,你就可以一直往前走,不用往两边看了。你可以理直气壮地忘掉verify()——要求我们懒惰如猪的程序员记住计算机可以搞定的东西是犯罪呀,对人民赤果果地犯罪!

好,现在我可以这么写了:

public class LionHeadTest extends PorkMockTest {
  public void testHuoHou() {
    LionHead head = mock(LionHead.class);
    head.bite();
    replay();
    cook(head);
    // 忘记吧,忘记吧。忘记是一种幸福。能忘记的人才能快乐地吃狮子头啊!
  }
}


欢乐的时光总是过得快,又到时间说白白。不要走开,广告之后请继续收看俺摸(mock),俺摸,俺默默摸!
   发表时间:2008-01-17  
不错!幽默!
0 请登录后投票
   发表时间:2008-01-17  
生动啊~~~喜欢那个
// 忘记吧,忘记吧。忘记是一种幸福。能忘记的人才能快乐地吃狮子头啊! 
呵呵
0 请登录后投票
   发表时间:2008-01-17  
忘记verify(),那如果写test的时候忘了assertXXX(...),那情形不是一样么。写Unit测试的第一步不是先写出测试失败的Unit Case么?
0 请登录后投票
   发表时间:2008-01-17  
kongxx 写道
忘记verify(),那如果写test的时候忘了assertXXX(...),那情形不是一样么。写Unit测试的第一步不是先写出测试失败的Unit Case么?


一样么?不一样吧?用assert,你要不就没写assert,也就是说你的需求意图根本没有表现在代码中,要不就写了这么一行来表达这个需求。

verify不同啊。你的需求是那些写在replay()之前的expectation。你期望foo()被调用,就写一个foo()。但是即使你根据需求写了这么多期望,如果你忘了verify(),也还是没用功。

打个比方,你要是忘了上班,不发你工资天公地道。但是如果你确实上班了,但是公司还要求你每天都到内部网上写一个“是的,我今天的工作不是无偿的,请付给我工资”,那么这个“忘记”就比较讨厌了是不?


我承认TDD会减轻“忘记”的问题。但是,不绝对吧?对下面一个测试:
int runFoo(Foo foo) {
  return 0;
}
void testFoo() {
  Foo foo = mock(Foo.class);
  expect(foo.run()).andReturn(1);
  replay();
  assertEquals(1, runFoo(foo));
}


它也变红,因为assertEquals()不通过。把runFoo()改成return foo.run(),就绿了。什么机会让你能够意识到verify()忘了呢?
0 请登录后投票
   发表时间:2008-01-18  
EasyMockTemplate 如何?
0 请登录后投票
   发表时间:2008-01-18  
hax 写道
EasyMockTemplate 如何?

不好,不好。一个replay()很清楚地标示了expectation。要是怕看不清楚,放几个空行和传说中的神秘分隔符也足够了。

匿名类的语法实在比较烦。不值当的。
0 请登录后投票
   发表时间:2008-01-20  
那是框架应该解决的问题,jmock就自动verify了。
0 请登录后投票
   发表时间:2008-01-21  
easymock2.0好像不支持对一个返回值为空的方法期望抛出一个异常,他的IExpectationSetters只有在返回值不为空的时候才能addThrow,这个有点很不爽!
0 请登录后投票
   发表时间:2008-01-21  
kjhot 写道
easymock2.0好像不支持对一个返回值为空的方法期望抛出一个异常,他的IExpectationSetters只有在返回值不为空的时候才能addThrow,这个有点很不爽!


EasyMock.expectLastCall().andThrow()?
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics