今天早上一时兴起,去网上下载下来JMock,EasyMock最新版本来玩玩。用来测试的行为很简单。就是有一个窗体,上面有一个文本框,一个按钮。如果点击按钮,就会把文本框的内容设置为“Hello”。应用MVP模式,这个行为应该在Presenter中,而View接口应该是这样的:
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<ActionListener> {
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框架,搞什么搞!简单一点不好么?