简体   繁体   English

Junit:测试用例的分离

[英]Junit: Separation of test cases

This is more of a junit logic question than anything else. 这比任何其他问题都更是一个junit逻辑问题。

The scenario I have is below: 我的情况如下:

I have a standalone java application that consumes data from a web service and saves data from that service on the client(where the application is running) machine. 我有一个独立的Java应用程序,该应用程序使用来自Web服务的数据,并将来自该服务的数据保存在客户端(运行应用程序的计算机)上。

This data is saved in the form of XML and is then read by another application to render that content on a thick client UI. 此数据以XML格式保存,然后由另一个应用程序读取以在胖客户端UI上呈现该内容。

The flow is depicted in the diagram below: 该流程如下图所示:

在此处输入图片说明

I wish to write unit tests for this flow but I fail to understand how to unit test the logic I have for consuming the webservices and then verifying that what was saved on the client's machine is correct. 我希望为此流程编写单元测试,但是我无法理解如何对使用Web服务的逻辑进行单元测试,然后验证保存在客户端计算机上的内容是否正确。

Another piece of the puzzle is how to verify(unit test) what was rendered on the UI with the XML that was saved on the client's machine. 另一个难题是如何使用保存在客户端计算机上的XML验证(单元测试)UI上呈现的内容。

I understand that each junit has to be as small as possible and should test the underlying functionality independently. 我知道每个junit必须尽可能小,并且应该独立测试基础功能。

The application will be hosted in a continuous integration environment like Hudson and would probably not provide rights to the application to write anything to that machine. 该应用程序将托管在像Hudson这样的持续集成环境中,并且可能不会提供该应用程序向该计算机写入任何内容的权利。 This complicates matters further. 这使事情进一步复杂化。

Any help would be appreciated. 任何帮助,将不胜感激。

In the above scenario, for the sake of simplicity, I have shown the standalone application and the thick client as separate but essentially they can be one application. 在上面的场景中,为简单起见,我将独立的应用程序和胖客户端显示为单独的,但实际上它们可以是一个应用程序。

The thick client that reads the XML data to display is coded in javafx. 读取要显示的XML数据的胖客户端是用javafx编码的。

You didn't specify what will serve as UI? 您没有指定将用作UI的内容吗? Will this be a GUI app or a web one ? 这将是GUI应用程序还是Web应用程序? That has some impact on the general approach but just to give some hints I will share my experience. 这对一般方法有一定影响,但只是给我一些提示,我将分享我的经验。 I did a lot gui apps using swing and that will be my baseline. 我使用swing做了很多gui应用程序,这将是我的基准。

So the trend is to make your GUI as thin as possible and put there logic that is only responsible for displaying components. 因此,趋势是使GUI尽可能地薄,并放置仅负责显示组件的逻辑。 To achieve that you can use well known design patterns like Presentation Model( http://martinfowler.com/eaaDev/PresentationModel.html ) or Passive View ( http://martinfowler.com/eaaDev/PassiveScreen.html ) 为此,您可以使用诸如Presentation Model( http://martinfowler.com/eaaDev/PresentationModel.html )或Passive View( http://martinfowler.com/eaaDev/PassiveScreen.html )之类的众所周知的设计模式。

Then you start testing your business logic. 然后,您开始测试业务逻辑。 General guidelines are: 一般准则是:

  • you don't use external resources ( Files, DBs etc.) instead you replace them with Fakes or Mocks which provide some fixed input for your tests. 您不使用外部资源(文件,数据库等),而是将其替换为Fakes或Mocks,它们为测试提供了一些固定的输入。
  • You test only the behaviour of the class not the behaviour of the dependencies 您仅测试类的行为,而不测试依赖项的行为
  • Try to avoid complex input 尽量避免复杂的输入
  • Using mentioned patterns you can mock your view layer and test only whether certain methods of view where called in synchrronization layer. 使用提到的模式,您可以模拟视图层,并仅测试某些视图方法是否在同步层中被调用。

I hope I helped. 希望我能帮上忙。 If not let me know if something is a bit fuzzy. 如果不是,请让我知道是否有些模糊。 It would be also good if you could give an example how would you like to tackle the problem and what are you concerns regarding your approach. 如果您能举一个例子,您将如何解决该问题以及您对方法的关注点也将是一件好事。

@Example @例

I don't have experience with JavaFx so I will try to show how I would do it in Swing. 我没有JavaFx的经验,所以我将尝试展示如何在Swing中进行操作。 This example assumes that you know what are mocks and what they are for. 此示例假定您知道什么是模拟以及它们的用途。

First let's figure out what is the most important functionality in thin client. 首先,让我们找出瘦客户机中最重要的功能。 I would go for something like that. 我会去那样的事情。 User opens xml file and app displays it in some form. 用户打开xml文件,然后应用以某种形式显示它。 (The form is not important it could be tree it could be a table or a grid whatever. Since it is view I don't care right now) (形式并不重要,它可以是树,也可以是表或网格。无论是它是视图,我现在都不在乎)

The basic scenario would be user selects a file, app opens that file and parses it and then the result is displayed. 基本方案是用户选择一个文件,应用程序打开该文件并进行解析,然后显示结果。 Let's call this scenario "Open Results". 我们将此场景称为“开放结果”。

First test: 第一次测试:

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

The purpose of this test is to check if OpenResultsAction will do the job. 该测试的目的是检查OpenResultsAction是否可以完成这项工作。 Here we are not testing what is parsed and whether gui has the correct data. 在这里,我们不测试解析的内容以及gui是否具有正确的数据。 We tests if certain methods on certain objects were called. 我们测试是否调用了某些对象上的某些方法。 This tests also specifies a contract between action class, view and applicationModel. 此测试还指定了操作类,视图和applicationModel之间的协定。 This is done via interfaces. 这是通过接口完成的。 So you can later provide concrete implementation which will be tested in next step. 因此,您以后可以提供具体的实现,将在下一步中进行测试。 Then I would provide implementation which I will skip to make this example as short as possible. 然后,我将提供实现,为使该示例尽可能简短,我将跳过该实现。

So what is next. 那么接下来是什么。 Since gui is not going to be tested at all I would go for ApplicationModel tests. 由于gui根本不会进行测试,因此我将进行ApplicationModel测试。 In first test we specified that applicationModelInterface should have method load(String filename); 在第一个测试中,我们指定applicationModelInterface应该具有方法load(String filename); And we will test if concrete implementation of it. 我们将测试它的具体实现。

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

What is XMLDocument ? 什么是XMLDocument? It stores content of xml file. 它存储xml文件的内容。 It can be represented as vector of file lines. 它可以表示为文件行的向量。 Our AppModelLoader will parse it to objects. 我们的AppModelLoader会将其解析为对象。 XFileLoader is another layer which allows me to get rid of file operations in my unit tests. XFileLoader是另一层,允许我在单元测试中摆脱文件操作。 Here it is mocked but in real app it should be replaced with somethind that will read in xml file and return XMLDocument. 这里是模拟的,但在实际应用中,应将其替换为将在xml文件中读取并返回XMLDocument的somethind。 Data is a class that would be used to store parsed data. 数据是用于存储已解析数据的类。 If content of xml is "<Person><name>Tom</name><age>34</age></Person> then Data would look like: 如果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
}

And that is basically it. 基本上就是这样。 Of course more tests are required like for example if view.getFileName() will return empty String in OpenResultsAction ( user hit cancel on JFileChooser ) then I need to verify if nothing else was called. 当然,还需要进行更多测试,例如,如果view.getFileName()将在OpenResultsAction中返回空String(用户在JFileChooser上单击cancel),那么我需要验证是否未调用其他函数。 If I would have all the classes tested then I would write GUI part and combine it. 如果我要测试所有的类,那么我将编写GUI部分并将其组合。

Let me know if that makes sense. 让我知道这是否有意义。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM