以前用过一些Mock框架;使用jmockit时,我以为mock框架都差不多,jmockit也不例外。吃了一些苦头才发现,jmockit是多么的与众不同(中性词).
在我看来,jmockit作者的思维方式是扭曲的,jmockit的用法也是诡异的。要想充分利用jmockit的强大功能,你必须容忍它与众不同的地方。本文给出一些最近收集到的诡异点:
Mock行为中的诡异点
看起来只Mock了一个对象,实际上Mock了整个类
@Mocked //看起来只Mock了bean这个对象 private SimpleBean bean; @Test public void test_MockedInstance() throws Exception { bean.throwException(); // 不会报异常,因为bean已经被mock了. 这个好理解. } @Test public void test_AnotherInstance() throws Exception { SimpleBean bean2 = new SimpleBean(); bean2.throwException(); // 也不会报异常,因为SimpleBean的任何一个实例都被Mock了。想不到吧? }
看起来只Mock了本类,实际上这个类的父类及所有祖先类都被Mock了
@Mocked private SubSimpleBean bean; //看起来只Mock了SubSimpleBean类 @Test public void test_AnotherInstance() throws Exception { SimpleBean bean2 = new SimpleBean(); //SubSimpleBean的父类 bean2.throwException(); // 不会报异常,因为SubSimpleBean的父类也被Mock了 (所有祖先类都被Mock, 但java.lang.Object除外) }
要Mock一个类,可以把这个类的实例作为测试方法的形式参数,也算是有创意了
@Test public void putTest(final Cache mockedCache) { //被mock对象作为方法的参数 new Expectations() { { mockedCache.put(bean); } }; .... }
Expectation中的诡异点
Expectation中不但做了mocking,也隐式地做了verifying
new Expectations() { { /** * 下面的代码做了两件事: * 1. mock dao的行为: dao.getNumOfBeans()如果被调用,则返回值是100<br/> * 2. 同时,期望dao.getNumOfBeans()在测试时被调用,如果没调用,意味测试未通过 */ dao.getNumOfBeans(); result = 100; } };
Expectioan里的隐式verification只跟被Mock的方法有关
@Test public void test_Not_Mocked_Object(){ Service service = new Service(); ... new Expectations(){{ nonMockedClass.someMethod(); //不会出错。虽然service.saveAndCache(bean)时不会调用NonMockedClass的方法,但由于NonMockedClass不是被mock类,jmockit不会验证跟它有关的语句 }}; service.saveAndCache(bean); }
Expectations录制了方法未执行,会报错;未录制但执行了,却不会报错
@Test public void test_Not_Invoked_Method(){ Service service = new Service(); service.setCache(cache); service.setDao(dao); final SimpleBean bean = new SimpleBean(); new Expectations(){{ mockedList.someMethod(); //会出错,因为service.saveAndCache(bean)时不需要调任何MockedList的方法 }}; service.saveAndCache(bean); } @Test public void test_NoCoverage(){ Service service = new Service(); service.setCache(cache); service.setDao(dao); new Expectations(){{ //测试可以通过,虽然这里没有录制任何被mock类的方法 }}; service.saveAndCache(new SimpleBean()); } @Test public void test_CoverOnlyOne(){ Service service = new Service(); service.setCache(cache); service.setDao(dao); final SimpleBean bean = new SimpleBean(); new Expectations(){{ //测试可以通过,虽然这里只录制了部分被mock类的方法 dao.save(bean); }}; service.saveAndCache(bean); }