public interface View { public void setText(String text); public void addActionListener(ActionListener actionListener); }
我这里就不TDD了,直接给出Presenter的实现,然后我们来看JMock和EasyMock如何处理这个问题。
public class Presenter { public Presenter(final View view) { view.addActionListener(new ActionListener() { public void actionPerformed() { view.setText("Hello"); } }); } }
好的,我先来写一段伪测试代码:
引用
创建MockView
创建Presenter,传以MockView做为参数
在MockView上触发事件
验证MockView的SetText方法被调用了
然后就用JMock来实现这段伪代码。
@Test public void test_click_button_should_set_text_hello() { Mockery mockery = new Mockery(); final View mockView = mockery.mock(View.class); final ActionListenerMatcher actionListenerMatcher = new ActionListenerMatcher(); mockery.checking(new Expectations() { { one(mockView).addActionListener( with(actionListenerMatcher)); one(mockView).setText("Hello"); } }); new Presenter(mockView); actionListenerMatcher.fireActionPerformed(); mockery.assertIsSatisfied(); }
由于JMock没有对触发事件提供直接的支持,所以是自己写了一个ActionListenerMatcher来达到这个目的的。这个Matcher的实现如下:
public class ActionListenerMatcher extends BaseMatcher{ private ActionListener actionListener; public boolean matches(Object item) { actionListener = (ActionListener) item; return true; } public void fireActionPerformed() { actionListener.actionPerformed(); } public void describeTo(Description description) { } }
可以看到之所以要这个Matcher是因为,我们需要有一个地方来保存Presenter传进来的listener,并且提供一个用来触发事件的方法。
EasyMock的实现与之非常类似,此处不再赘述。代码可以参见附件。
我们可以看到什么?我看到的是,测试时候的Intention(意图)完全掩盖在冗长复杂的代码之中了。如果不用Mock该怎么做?很简单:
@Test public void test_click_button_should_set_text_hello() { MockView mockView = new MockView(); new Presenter(mockView); mockView.fireActionPerformed(); Assert.assertEquals("Hello", mockView.getText()); }
这段代码是不是和上面的伪代码一模一样?MockView是这样的:
private class MockView implements View { private ActionListener actionListener; private String text; public void addActionListener(ActionListener actionListener) { this.actionListener = actionListener; } public void setText(String text) { this.text = text; } public String getText() { return text; } public void fireActionPerformed() { actionListener.actionPerformed(); } }
做完这个实验,我不得不说。Mock框架,搞什么搞!简单一点不好么?