2

私は多数の UI コンポーネントを含むプロジェクトに取り組んできました。すべてのコンポーネントは MVC パターンに基づいているため、コンポーネント (パブリック インターフェイスとファクトリ、パッケージで保護されたモデル/ビュー/コントローラー) として構造化されています。

それらを「手で」テストする - モッキング手法を使用するのは難しすぎて時間がかかります。

それで、私は Fest フレームワーク - http://fest.easytesting.org/に飛び込みました。それは簡単で、よく、仕事をします。

JMockit - http://code.google.com/p/jmockit/と Fest の両方を使用しようとすると、問題が発生します。私は、Fest が JMockit と衝突する可能性のあるいくつかのライブラリ (リフレクションとアサート) を使用していることに気付きました。

テストを実行すると、JMockit は必要なクラスをモックしません。私は以前に JMockit を使用していたので、ライブラリ間の何らかの衝突であると確信しています。モックされたクラスで生成される $Proxy$ はありません。もちろん、クラスは正しく動作しません。

完全なコンポーネントの相互作用をテストする必要があるため、モッキングが必要です!

バージョン:

Jモキット: 0.999.8

祭り:

Fest-swing 1.2.1 Fest-assert 1.4 Fest-util 1.1.6 Fest-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:

ありがとう、ロジェリオ。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 違反を忘れないでください。

4

1 に答える 1

1

問題は、このメソッド呼び出しの予期がテストに記録される前に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");
   }
}
于 2013-01-24T10:59:11.480 に答える