I have been working on a project that has numerous UI components. Since all the components are based on the MVC pattern, they are structured as a component - public interface and factory, package protected model/view/controller.
Testing them "by hand" - using mocking tecniques is too difficult and time consuming.
So i popped in the Fest framework - http://fest.easytesting.org/.
It's simple, good and does the job.
The problem appears when I try to use both JMockit - http://code.google.com/p/jmockit/ and Fest together. I noticed that Fest uses some libraries that could collide with JMockit - reflection and assert.
When i run the test, JMockit doesn't mock the required class. I used JMockit before so I'm pretty sure it's some sort of collision between the libraries. There is no $Proxy$ generated on the mocked class, and of course, the class misbehaves.
Mocking IS required, since I have to test the full component interaction!
Versions:
JMockit:
0.999.8
Fest:
Fest-swing 1.2.1
Fest-assert 1.4
Fest-util 1.1.6
Fest-reflect 1.2
I have no intention to go conflict hunting by looking both libraries, so any help would be appretiated.
Thanks.
UPDATE:
The test/sample code is here:
public interface SimpleControllable {
void init();
JPanel getPanel();
}
public class SimpleController implements SimpleControllable {
private final SimpleForm simpleForm;
private final SimpleModel simpleModel;
public SimpleController(
final SimpleForm simpleForm,
final SimpleModel simpleModel
) {
this.simpleForm = simpleForm;
this.simpleModel = simpleModel;
}
@Override
public void init() {
simpleForm.init();
//This works!
/*simpleForm.getTestButton().addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
simpleForm.getTestButton().setText(simpleModel.getSimpleValue());
}
});*/
//This doesn't!
simpleForm.getTestButton().setText(simpleModel.getSimpleValue());
}
@Override
public JPanel getPanel() {
return simpleForm.getTestPanel();
}
}
public class SimpleModel {
private final SimpleServable simpleServable;
public SimpleModel(final SimpleServable simpleServable) {
this.simpleServable = simpleServable;
}
public String getSimpleValue() {
return simpleServable.getValue();
}
}
public interface SimpleServable {
String getValue();
}
public class SimpleService implements SimpleServable {
private String value;
@Override
public String getValue() {
return value;
}
}
public class SimpleForm {
private JButton testButton;
private JPanel testPanel;
public void init() {
testPanel.setName("testPanel");
testButton.setName("testButton");
}
public JButton getTestButton() {
return testButton;
}
public JPanel getTestPanel() {
return testPanel;
}
}
public class SimpleTest extends FestSwingJUnitTestCase {
@Mocked
private SimpleService simpleServable;
private FrameFixture window;
@Override
protected void onSetUp() {
FailOnThreadViolationRepaintManager.install();
JFrame frameUi = GuiActionRunner.execute(new GuiQuery<JFrame>() {
protected JFrame executeInEDT() {
SimpleControllable simpleControllable = getSimpleControllable();
JFrame frame = new JFrame("TEST");
frame.add(simpleControllable.getPanel());
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.pack();
return frame;
}
});
robot().settings().delayBetweenEvents(1000);
// IMPORTANT: note the call to 'robot()'
// we must use the Robot from FestSwingTestngTestCase
window = new FrameFixture(robot(), frameUi);
window.show(); // shows the frameU
}
//Should use factory, but not neccesary for test purposes...
private SimpleControllable getSimpleControllable() {
SimpleForm simpleForm = new SimpleForm();
//SimpleServable simpleServable = new SimpleService();
SimpleModel simpleModel = new SimpleModel(simpleServable);
SimpleControllable simpleControllable = new SimpleController(
simpleForm,
simpleModel
);
//Initialize the component, therein lies the problem...
simpleControllable.init();
return simpleControllable;
}
@Test
public void test() throws Exception {
//Before
new Expectations() {
{
simpleServable.getValue();
result = "TEST";
}
};
//When
//This works!
// window.panel("testPanel").button("testButton").click().requireText("TEST");
//This doesn't!
window.panel("testPanel").button("testButton").requireText("TEST");
//Then
}
}
It seems that I blamed the framework too early. But I still don't understand the details why it doesn't work. The class Service IS mocked, and it should still be usable, even after delaying the expectations. I understand the time problem(initialization of the component), but have no idea how to "fix" this.
Thanks.
UPDATE2:
Thanks, Rogerio.
You can test the component with FEST, but it doesn't really take advantage of JMockit, and there are classes that have quite a lot of methods(yes, I know - SRP, but let's try stay on this path) and would benefit greatly from a mocking framework such as JMockit. I used this before posting the question here, so you can use it yourself, and understand that this is not the way I want to go:
public class SimpleTest extends FestSwingJUnitTestCase {
//This works, and I used this mechanism before, but this is without the help of JMockit.
//If you have a lot of methods you want to mock this test turns into chaos.
public static class MockSimpleServable implements SimpleServable{
@Override
public String getValue() {
return "TEST";
}
}
// @Mocked
// private SimpleServable simpleServable;
private FrameFixture window;
@Override
protected void onSetUp() {
FailOnThreadViolationRepaintManager.install();
JFrame frameUi = GuiActionRunner.execute(new GuiQuery<JFrame>() {
protected JFrame executeInEDT() {
SimpleControllable simpleControllable = getSimpleControllable();
JFrame frame = new JFrame("TEST");
frame.add(simpleControllable.getPanel());
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.pack();
return frame;
}
});
robot().settings().delayBetweenEvents(1000);
// IMPORTANT: note the call to 'robot()'
// we must use the Robot from FestSwingTestngTestCase
window = new FrameFixture(robot(), frameUi);
window.show(); // shows the frameU
}
//Should use factory, but not neccesary for test purposes...
private SimpleControllable getSimpleControllable() {
SimpleForm simpleForm = new SimpleForm();
//SimpleServable simpleServable = new SimpleService();
SimpleServable simpleServable = new MockSimpleServable();
SimpleModel simpleModel = new SimpleModel(simpleServable);
SimpleControllable simpleControllable = new SimpleController(
simpleForm,
simpleModel
);
//Initialize the component, therein lies the problem...
simpleControllable.init();
return simpleControllable;
}
@Test
public void test() throws Exception {
//This works!
// window.panel("testPanel").button("testButton").click().requireText("TEST");
//This doesn't!
window.panel("testPanel").button("testButton").requireText("TEST");
}
}
The question remains - does anybody know some way I could test this with JMockit, don't forget the EDT violation.