Junit:分离测试用例

时间:2013-10-25 18:57:34

标签: java unit-testing junit continuous-integration junit4

这更像是一个junit逻辑问题而不是其他任何东西。

我的情景如下:

我有一个独立的java应用程序,它使用来自Web服务的数据,并在客户端(运行应用程序的地方)机器上保存该服务的数据。

此数据以XML格式保存,然后由另一个应用程序读取,以在胖客户端UI上呈现该内容。

流程如下图所示:

enter image description here

我希望为此流程编写单元测试,但我无法理解如何对使用Web服务的逻辑进行单元测试,然后验证客户端计算机上保存的内容是否正确。

另一个难题是如何使用保存在客户端计算机上的XML来验证(单元测试)在UI上呈现的内容。

我知道每个junit必须尽可能小,并且应该独立测试底层功能。

应用程序将托管在像Hudson这样的持续集成环境中,并且可能无法为应用程序提供向该计算机写入任何内容的权限。这使问题更加复杂。

任何帮助都将不胜感激。

在上面的场景中,为了简单起见,我将独立应用程序和胖客户端显示为单独的,但实质上它们可以是一个应用程序。

读取要显示的XML数据的胖客户端以javafx编码。

1 个答案:

答案 0 :(得分:3)

您没有指定将用作UI的内容?这是GUI应用程序还是Web应用程序? 这对一般方法有一些影响,但只是给出一些提示,我将分享我的经验。我使用swing做了很多gui应用程序,这将是我的基线。

因此,趋势是让您的GUI尽可能地薄,并放置仅负责显示组件的逻辑。为此,您可以使用众所周知的设计模式,如演示模型(http://martinfowler.com/eaaDev/PresentationModel.html)或被动视图(http://martinfowler.com/eaaDev/PassiveScreen.html

然后开始测试您的业务逻辑。一般准则是:

  • 您不使用外部资源(文件,数据库等),而是将它们替换为为您的测试提供一些固定输入的Fakes或Mocks。
  • 您只测试类的行为而不是依赖项的行为
  • 尽量避免复杂输入
  • 使用提到的模式,您可以模拟视图层,并仅测试在同步层中调用的某些视图方法。

我希望我帮助过。如果没有让我知道是否有点模糊。如果您能举例说明您希望如何解决问题以及您对自己的方法有何顾虑,那也很好。

@举例

我没有JavaFx的经验,所以我将尝试展示如何在Swing中完成它。 这个例子假设您知道什么是模拟以及它们的用途。

首先让我们弄清楚瘦客户端中最重要的功能是什么。我会选择那样的东西。用户打开xml文件,应用程序以某种形式显示它。 (形式并不重要,它可能是树,它可以是一个桌子或网格。因为它是视图我现在不在乎)

基本方案是用户选择文件,应用程序打开该文件并解析它,然后显示结果。我们称这个场景为“开放结果”。

第一次测试:

class OpenResultsShould{
    @Test
    public void loadResults() {
        Data fake = mock (Data.class);
        ViewInterface view = mock(ViewInterface.class); // mocking view
        when(view.getFilename()).thenReturn("file.xml"); // we specify that when getFileName() method of view mock will be called "file.xml" string will be returned.

        ApplicationModelInterface appModel = mock(ApplicationModelInterface.class); // mocking app model
        when(appModel.getDataForView()).thenReturn(fake);

    OpenResultsAction openResults = new OpenResultsAction( view, appModel );

        openResults.actionPerformed(new ActionEvent());

        verify(view).getFileName();           // checks that view.getFileName was called within actionPerdormed()
        verify(appModel).load("file.xml");    // check that appModel.load( ) with file.xml as parameter was called within actionPerformed()
        verify(appModel).getDataForView();    // similar to above
        verify(view).loadDataFromModel( fake ); // finally I check if loadDataFromModel on view was called.
    }
}

此测试的目的是检查OpenResultsAction是否可以完成这项工作。在这里,我们不测试解析的内容以及gui是否具有正确的数据。我们测试是否调用某些对象的某些方法。此测试还指定了action类,view和applicationModel之间的契约。这是通过接口完成的。因此,您可以在以后提供将在下一步中进行测试的具体实现。 然后我会提供实现,我将跳过这个实例尽可能短。

接下来是什么。由于gui根本不会进行测试,我会去ApplicationModel测试。在第一个测试中,我们指定applicationModelInterface应该有方法加载(String filename);我们将测试它是否具体实现。

class ApplicationModelShould{
    @Test
    public void loadModelFromFile() {
        XMLDocument xml = new XMLDocumentFake();
        XMLFileLoader xFileLoader = mock(XMLFileLoader.class);
        when(xFileLoader.load("file.xml").thenReturn( xml );
        ApplicationModelInterface appModel = new ConcreteApplicationModel( new FileLoaderFake() );

        appModel.load("plik.xml"); // it should call xFileLoader and then parse returned xml document.
        doReturn(xml).when(xFileLoader).load("plik.xml"); // verifies if xFileLoader returned xml when appModel.load called it's load method. 
       Data expectedResult = populateExpectedResults();
       assertEquals( appModel.getDataForView().equals( expectedResult ) );
    }
}

什么是XMLDocument?它存储xml文件的内容。它可以表示为文件行的向量。我们的AppModelLoader会将其解析为对象。 XFileLoader是另一个允许我在单元测试中摆脱文件操作的层。在这里它被模拟,但在实际的应用程序中,它应该被替换为将在xml文件中读取并返回XMLDocument的somethind。数据是一个用于存储已分析数据的类。如果xml的内容是“< Person>< name> Tom< / name>< age> 34< / age>< / Person>那么数据将如下所示:

class Data{
    private Person person;
    Data(Person person){ this.person = person; }
    ...
};

class Person{
    private String name;
    private int age;
    .... setters, getters and constructors
}

基本上就是这样。当然需要更多的测试,例如,如果view.getFileName()将在OpenResultsAction中返回空String(用户在JFileChooser上点击取消),那么我需要验证是否还没有其他人被调用。如果我将测试所有类,那么我将编写GUI部分并将其组合。

如果这有意义,请告诉我。