上一篇中我简单介绍了一下Mock ,今天我就来说明一下怎么借助easymock来进行GUI的测试。
关于easymock的安装请参见它的说明文档,这里就不再介绍了。
在进行测试前,我们先要有个问题要解决:那就是怎么在测试代码中得到GUI中的各个组件(比如JButton)?也许你会说将它们定义为public就可以了,但这却不太符合面向对象中的封装思想。用反射?也不错,但好像太复杂了,为了一个测试好像没有必要。当然我们可以利用一些其他的辅助测试工具,比如JFCUnit,这些我会在下一篇中介绍。现在,就假设我们不知道有第三方的其他工具,那怎么办呢?我的做法是借用JavaBean的思想——提供一个get方法。
好了,下面就开始具体的操作了。
首先,我们就假设要测试的GUI定义一个接口,简单点,如下示:
package TestGUI;
import java.util.*;
public interface ListEditor {
Vector getNames();
void add(String name);
void delete(int index);
}
import java.util.*;
public interface ListEditor {
Vector getNames();
void add(String name);
void delete(int index);
}
然后,我们就可以写测试程序了(假如你采用的是TDD的话),但我们这里只是要演示easymock在测试GUI中的用处,所以我们不这样做,我们先写好GUI的代码,如下:
package TestGUI;
import javax.swing.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
public class ListWindow
extends JFrame {
private JList nameList;
private JButton addButton;
private ListEditor myEditor;
private JTextField nameField;
public ListWindow(ListEditor myEditor) {
super();
this.myEditor = myEditor;
}
extends JFrame {
private JList nameList;
private JButton addButton;
private ListEditor myEditor;
private JTextField nameField;
public ListWindow(ListEditor myEditor) {
super();
this.myEditor = myEditor;
}
public JList getNameList() {
return nameList;
}
return nameList;
}
public JButton getAddButton() {
return addButton;
}
return addButton;
}
public JTextField getNameField() {
return nameField;
}
return nameField;
}
public void init() {
setLayout();
initList();
initAddButton();
initField();
this.setSize(300, 200);
// this.pack();
}
setLayout();
initList();
initAddButton();
initField();
this.setSize(300, 200);
// this.pack();
}
private void setLayout() {
this.getContentPane().setLayout(new FlowLayout());
}
this.getContentPane().setLayout(new FlowLayout());
}
private void initList() {
nameList = new JList(getNames());
JScrollPane scroll = new JScrollPane(nameList);
this.getContentPane().add(scroll);
}
nameList = new JList(getNames());
JScrollPane scroll = new JScrollPane(nameList);
this.getContentPane().add(scroll);
}
private void initAddButton() {
addButton = new JButton("Add");
addButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
myEditor.add(nameField.getText());
movieList.setListData(getNames());
}
});
this.getContentPane().add(addButton);
}
addButton = new JButton("Add");
addButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
myEditor.add(nameField.getText());
movieList.setListData(getNames());
}
});
this.getContentPane().add(addButton);
}
private void initField() {
movieField = new JTextField(13);
this.getContentPane().add(nameField);
}
movieField = new JTextField(13);
this.getContentPane().add(nameField);
}
private Vector getNames() {
Vector names = myEditor.getNames();
return (names == null) ? new Vector() : names;
}
}
Vector names = myEditor.getNames();
return (names == null) ? new Vector() : names;
}
}
好了,现在一切都准备好了,我们可以来写测试了,如下:
package TestGUI;
import junit.framework.*;
import org.easymock.*;
import javax.swing.*;
import org.easymock.*;
import javax.swing.*;
public class TestBaseGUI
extends TestCase {
private MockControl control;
private ListEditor mockEditor;
private ListWindow window;
protected void setUp() throws Exception {
super.setUp();
control = MockControl.createNiceControl(ListEditor.class);
mockEditor = (ListEditor) control.getMock();
control.replay();
window = new ListWindow(mockEditor);
window.init();
window.show();
}
extends TestCase {
private MockControl control;
private ListEditor mockEditor;
private ListWindow window;
protected void setUp() throws Exception {
super.setUp();
control = MockControl.createNiceControl(ListEditor.class);
mockEditor = (ListEditor) control.getMock();
control.replay();
window = new ListWindow(mockEditor);
window.init();
window.show();
}
public void testList() {
JList nameList = window.getNameList();
assertNotNull("list not null", nameList);
assertTrue("is showing", nameList.isShowing());
}
public void testAddButton()
{
JButton addButton=window.getAddButton() ;
assertEquals("should be add","Add",addButton.getText() );
}
public void testField()
{
JTextField nameField=window.getNameField() ;
assertNotNull("text not null",nameField);
}
protected void tearDown() throws Exception {
super.tearDown();
}
JList nameList = window.getNameList();
assertNotNull("list not null", nameList);
assertTrue("is showing", nameList.isShowing());
}
public void testAddButton()
{
JButton addButton=window.getAddButton() ;
assertEquals("should be add","Add",addButton.getText() );
}
public void testField()
{
JTextField nameField=window.getNameField() ;
assertNotNull("text not null",nameField);
}
protected void tearDown() throws Exception {
super.tearDown();
}
}
这个测试非常简单,它只是测试GUI上的组件是否真的存在(非空)。本来没有什么意思,但之所以这么简单就是为了更集中的演示easymock的使用。在上面的代码中,我们首先用easymock中MockControl的静态工厂得到了一个MockControl(而且是nice的,这就使你不用去关心它的返回值),然后利用control我们就得到了一个mock并将它强制转换为ListEditor,然后我们就可以用它了(在使用前最好用replay激活它)。就是这么简单,如果这个测试通过了,那说明你的GUI就没有问题了,在具体用到项目中时,你只需要实现一个类,让它implements接口ListEditor ,然后将它传给GUI就可以了。
下面的测试更复杂点,它测试了GUI中各组件的功能,但原理是一样的:
package TestGUI;
import junit.framework.*;
import org.easymock.*;
import java.util.*;
import javax.swing.*;
import org.easymock.*;
import java.util.*;
import javax.swing.*;
public class TestMovieListWindow
extends TestCase {
private ListWindow ListWindow = null;
private static final String addString = "Test a mock!";
private Vector name = null;
private MockControl control = null;
private ListEditor mockEditor = null;
extends TestCase {
private ListWindow ListWindow = null;
private static final String addString = "Test a mock!";
private Vector name = null;
private MockControl control = null;
private ListEditor mockEditor = null;
protected void setUp() throws Exception {
super.setUp();
/**@todo verify the constructors*/
name = new Vector() {
{
add("Hello");
add("Hit");
}
};
control = MockControl.createControl(ListEditor.class);
mockEditor = (ListEditor) control.getMock();
}
super.setUp();
/**@todo verify the constructors*/
name = new Vector() {
{
add("Hello");
add("Hit");
}
};
control = MockControl.createControl(ListEditor.class);
mockEditor = (ListEditor) control.getMock();
}
public void testList() {
mockEditor.getNames();
control.setReturnValue(name, 1);
control.replay();
window = new ListWindow(mockEditor);
window.init();
window.show();
JList nameList = window.getNameList();
ListModel nameListModel = nameList.getModel();
assertEquals("Wrong size", name.size(), nameListModel.getSize());
assertEquals("should be hello!", "Hello", nameListModel.getElementAt(0));
control.verify();
}
mockEditor.getNames();
control.setReturnValue(name, 1);
control.replay();
window = new ListWindow(mockEditor);
window.init();
window.show();
JList nameList = window.getNameList();
ListModel nameListModel = nameList.getModel();
assertEquals("Wrong size", name.size(), nameListModel.getSize());
assertEquals("should be hello!", "Hello", nameListModel.getElementAt(0));
control.verify();
}
public void testAdd() {
Vector NameWithAddition = new Vector(name);
NameWithAddition.add(addString);
mockEditor.getNames();
control.setReturnValue(name, 1);
mockEditor.add(addString);
control.setVoidCallable(1);
mockEditor.getNames();
control.setReturnValue(NameWithAddition, 1);
control.replay();
window = new ListWindow(mockEditor);
window.init();
window.show();
JTextField nameField = window.getNameField();
nameField.setText(addString);
JButton addButton = window.getAddButton();
addButton.doClick();
JList nameList = window.getNameList();
ListModel nameListModel = nameList.getModel();
assertEquals("Wrong size", NameWithAddition.size(),
nameListModel.getSize());
assertEquals("should be Test a mock!", addString,
nameListModel.getElementAt(2));
control.verify();
}
Vector NameWithAddition = new Vector(name);
NameWithAddition.add(addString);
mockEditor.getNames();
control.setReturnValue(name, 1);
mockEditor.add(addString);
control.setVoidCallable(1);
mockEditor.getNames();
control.setReturnValue(NameWithAddition, 1);
control.replay();
window = new ListWindow(mockEditor);
window.init();
window.show();
JTextField nameField = window.getNameField();
nameField.setText(addString);
JButton addButton = window.getAddButton();
addButton.doClick();
JList nameList = window.getNameList();
ListModel nameListModel = nameList.getModel();
assertEquals("Wrong size", NameWithAddition.size(),
nameListModel.getSize());
assertEquals("should be Test a mock!", addString,
nameListModel.getElementAt(2));
control.verify();
}
protected void tearDown() throws Exception {
super.tearDown();
}
super.tearDown();
}
}
在这个测试中,我们返回的control不是nice的,所以你在每次设置时,都要说明它的返回值和调用的次数,其余的就没有什么变化了。
关于easymock更多的,请参看它提供的文档。
这次,我们用一种不太优雅的方式利用easymock进行了GUI的测试,下一次,我将演示用一种更好方式来进行测试,同时会介绍有关“超瘦”GUI的概念!
(代码在Jbuilder2005+xp sp1下通过)