浏览 6943 次
锁定老帖子 主题:俺摸,俺摸,俺默默摸 (2)
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2008-01-17
人家没那么说啦!讨厌!是刚才广告里说的嘛!我说刚才我们“俺摸”系列说到哪了? 对了,说到我们可以这么用PorkMockTest: public class LionHeadTest extends PorkMockTest { public void testHuoHou() { LionHead head = mock(LionHead.class); head.bite(); replay(); cook(head); // 忘记吧,忘记吧。忘记是一种幸福。能忘记的人才能快乐地吃狮子头啊! } } 那要是俺老猪比较馋(好像不需要“要是”了?),就好狮子头这口,连写了八个test都要mock(LionHead.class)怎么办?俺以前都这么写: public class LionHeadTest extends PorkMockTest { private final LionHead head = mock(LionHead.class); public void testEat() {...} public void testEatAgain() {...} public void testOneMorePlease() {...} ... } 不过后来在喜欢作JUnit专家状的沙师弟的唠叨下,已经不敢再明目张胆地这么做了。据丫说吧:“JUnit 3.8下,所有TestCase的构造函数都是在TestSuite创建的时候调用的,所以不要在构造函数里做任何可能比较慢或者可能抛exception的动作,否则,一旦发生异常,错误信息是惨不忍睹地不知所云!!!”。在Google上搜索出来的结果也似乎站在这个喜欢冒充高手的家伙那边,让我真想打他的脸啊,打他的脸。 哎,我真想揪住某个洋洋得意写了《Test Driven Development》的人的领口,啐他一脸。凭啥非要恶心俺老猪?招你啦? 于是,俺只好捏着鼻子在这个讨厌的家伙的监督押送下返工: class LionHeadTest extends PorkMockTest { private LionHead head; @Override protected void setUp() throws Exception { super.setUp(); head = mock(LionHead.class); } ... } 靠!这是一砣什么屎?于是,除非实在饿的不行了,俺老猪宁可在每个test里面调用一遍mock(LionHead.class)也懒得搞这么一砣东西出来。 这不?昨天听师傅讲楞加经(楞要加塞儿经?),迷迷糊糊又睡着了,等猴哥拎着耳朵(当然是他自己的耳朵,敢拎我的?哼哼,现在俺这肉可贵了,掂量着自己赔的起先!)把俺叫醒,正好听见师傅不小心把经文夹缝里的字儿给念出来几个。然后俺就觉得小腹一股热气冲上丹田,瞬间增加了几个甲子的功力。然后体力,智力,根骨,悟性都增加5点。任督二脉无师自通。这个讨厌的问题也终于找到了一个解决方案,哈哈。 长话短说,我想到了用annotation的方法。只要在PorkMockTest里面加上一点反射的咚咚,我就可以这么写了: public class LionHeadTest extends PorkMockTest { @Mock private LionHead head; public void testEat() {...} public void testEatAgain() {...} public void testOneMorePlease() {...} ... } 比我原来的那个版本还爽,现在我都不用车轱辘话把LionHead说两遍了!那么PorkMockTest怎么搞的呢?南无阿弥陀佛: @Target(FIELD) @Retention(RUNTME) protected @interface Mock {} @Override public void runBare() { mockFields(getClass(), this); super.runBare(); } private void mockFields(Class cls, Object obj) { Field[] fields = cls.getDecalredFields(); for (Field field : fields) { if (field.getAnnotation(Mock.class) != null) { field.setAccessible(true); field.set(obj, mock(field.getType()); } } Class superclass = cls.getSuperclass(); if (superclass != null) { mockFields(superclass, obj); } } 几个可能的问题回答一下: 1。为什么用runBare()而不是runTest()?因为我希望所有的@Mock fields在setUp()之前就初始化好,这样子类在setUp()里面就可以为所欲为了。 2。这样的速度是不是比较慢?毕竟每个test都要跑一下runBare(),也就都要跑一下这些反射代码?是的。真实代码是用了一个cache来缓存每个class的所有@Mock field。不过这种技术细节我就不展开了。 现在的PorkMockClass不要太好用,尤其是需要用到很多mock的类。老猪俺想吃啥就mock啥。什么龙虾,鲍鱼,燕窝,象拔蚌,都不在话下。 public class FeastTest extends PorkMockTest { @Mock private Lobster lobster; @Mock private Oyster oyster; @Mock private BirdNest nest; @Mock private ElephantNoseClam clam; ... } 俺摸,俺摸,俺使劲儿摸!哈哈。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2008-01-17
引用 真实代码是用了一个cache来缓存每个class的所有@Mock field。不过这种技术细节我就不展开了。 1、如果使用cache的话,如果保证不同的测试方法不会有关联性? 比如第一个测试方法中猪肉已经被咬一口了,第二个测试方法需要的是 完整的猪肉,怎么恢复呢? 2、cache的控制是简单还是复杂?如果是复杂,还是用简单的方式吃猪肉 比较爽:) |
|
返回顶楼 | |
发表时间:2008-01-17
yananay 写道 引用 真实代码是用了一个cache来缓存每个class的所有@Mock field。不过这种技术细节我就不展开了。 1、如果使用cache的话,如果保证不同的测试方法不会有关联性? 比如第一个测试方法中猪肉已经被咬一口了,第二个测试方法需要的是 完整的猪肉,怎么恢复呢? 2、cache的控制是简单还是复杂?如果是复杂,还是用简单的方式吃猪肉 比较爽:) cache的接口: Field[] getMockFields(Class<? extends TestCase>); setAccessible()很昂贵地,所以节省还是很可观地。 |
|
返回顶楼 | |
发表时间:2008-01-18
可是你还是没回答我的第一个问题,既然有了cache,怎么保证第2个测试
一定能得到一个完整的猪肉? |
|
返回顶楼 | |
发表时间:2008-01-18
yananay 写道 可是你还是没回答我的第一个问题,既然有了cache,怎么保证第2个测试
一定能得到一个完整的猪肉? cache只cache Field[]。每个测试实例都会被注射独立的mock instances。 |
|
返回顶楼 | |
发表时间:2008-01-18
ajoo 写道 yananay 写道 可是你还是没回答我的第一个问题,既然有了cache,怎么保证第2个测试
一定能得到一个完整的猪肉? cache只cache Field[]。每个测试实例都会被注射独立的mock instances。 这么说cache所作的只是 setAccessible ? 使用的时候再生成需要的 instance? 我的理解没错吧 最重要的是,不明白为什么放在 runBare 里就比放在 setup 里爽了? |
|
返回顶楼 | |
发表时间:2008-01-18
yananay 写道 ajoo 写道 yananay 写道 可是你还是没回答我的第一个问题,既然有了cache,怎么保证第2个测试
一定能得到一个完整的猪肉? cache只cache Field[]。每个测试实例都会被注射独立的mock instances。 这么说cache所作的只是 setAccessible ? 使用的时候再生成需要的 instance? 我的理解没错吧 最重要的是,不明白为什么放在 runBare 里就比放在 setup 里爽了? 缓存了Class -> Field[]这个过程,也缓存了setAccessible。这两个过程都比较昂贵的。使用的时候再生成mock instance。 放在setUp()里,害怕子类覆盖的时候忘了super.setUp()。 而runBare()一般没人会override的。 |
|
返回顶楼 | |