JMockit and Fest UI testing



我一直在做一个有很多UI组件的项目。因为所有的组件都是基于MVC模式的,所以它们被构造成一个组件——公共接口和工厂,包保护的模型/视图/控制器。

"手工"测试它们——使用模拟技术太困难且耗时。

于是我打开了Fest框架- http://fest.easytesting.org/。这很简单,很好,而且很有效。

当我尝试同时使用JMockit - http://code.google.com/p/jmockit/和Fest时,问题出现了。我注意到Fest使用了一些可能与JMockit冲突的库——反射和断言。

当我运行测试时,JMockit不模拟所需的类。我以前使用过JMockit,所以我很确定这是库之间的某种冲突。在被模拟的类上没有生成$Proxy$,当然,类的行为会出错。

mock是必需的,因为我必须测试完整的组件交互!

版本:

JMockit:0.999.8

节:

Fest-swing 1.2.1Fest-assert 1.4Fest-util 1.1.6Fest-reflect 1.2

我不打算通过查看两个库来寻找冲突,所以任何帮助都是非常感谢的。

谢谢。

更新:

测试/示例代码在这里:

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
    }
}

我似乎太早责备了框架。但我仍然不明白为什么它不起作用的细节。类Service是模拟的,即使延迟了预期,它仍然应该是可用的。我理解时间问题(组件的初始化),但不知道如何"修复"这个

谢谢。

更新2:

谢谢,Rogerio。您可以使用FEST测试组件,但它并没有真正利用JMockit,而且有些类具有相当多的方法(是的,我知道- SRP,但让我们尝试保持这种方式),并且将从JMockit这样的模拟框架中受益匪浅。我在这里发布问题之前使用了这个,所以你可以自己使用它,并理解这不是我想要的方式:

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");
    }
}

问题仍然存在-有没有人知道我可以用JMockit测试这个的方法,不要忘记EDT冲突

问题在测试代码中,它在窗口构建期间碰巧调用了SimpleServable#getValue()之前,该方法调用的期望被记录在测试中。

要解决这个问题,只需将new Expectation() {{ ... }}块移动到onSetUp()方法,将其放在对GuiActionRunner.execute(GuiQuery)的调用之前。

以下代码的简化版本演示了它:
public final class SimpleForm
{
    final JPanel testPanel;
    final JButton testButton;
    public SimpleForm()
    {
        testPanel = new JPanel();
        testPanel.setName("testPanel");
        testButton = new JButton();
        testButton.setName("testButton");
        testPanel.add(testButton);
    }
}
public final class SimpleService {
    public String getValue() { return null; }
}
public final class SimpleController
{
   private final SimpleForm form;
   public SimpleController()
   {
      form = new SimpleForm();
      SimpleService service = new SimpleService();
      form.testButton.setText(service.getValue());
   }
   public JPanel getPanel() { return form.testPanel; }
}
public final class SimpleTest extends FestSwingJUnitTestCase
{
   FrameFixture window;
   @NonStrict SimpleService service;
   @Override
   protected void onSetUp()
   {
      FailOnThreadViolationRepaintManager.install();
      // Record expectations *before* they are replayed:
      new Expectations() {{ service.getValue(); result = "TEST"; }};
      JFrame frameUi = GuiActionRunner.execute(new GuiQuery<JFrame>() {
         @Override
         protected JFrame executeInEDT()
         {
            SimpleController controller = new SimpleController();
            JFrame frame = new JFrame("TEST");
            frame.add(controller.getPanel());
            frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            frame.pack();
            return frame;
         }
      });
      robot().settings().delayBetweenEvents(1000);
      window = new FrameFixture(robot(), frameUi);
      window.show();
   }
   @Test
   public void test()
   {
      // This works provided "service.getValue()" gets called *after* 
      // expectations are recorded. With the call happening during the
      // creation of the window, it must be recorded at the beginning
      // of the "onSetUp" method.
      window.panel("testPanel").button("testButton").requireText("TEST");
   }
}

相关内容

  • 没有找到相关文章