SECCIONES
AÑADIR ARTÍCULO
CLUB TÉCNICO
CURSOS
WEB AMIGAS
| Fecha de publicación: 2008.04.09 | |
| Grado de Dificultad: 3 | |
iValidator is an open source test automation framework, designed especially for integration tests. Guenter, in this article, motivates why there is a need for a new framework and how it is used. iValidator is an open source test automation framework, designed especially for integration tests. The article motivates why there is a need for a new framework and how it is used. We started to build the iValidator framework for integration tests when we were asked to automate the test of a complex application (running a fully automated container terminal) since it tooks the test team several days to test the system manually for each release. The system was composed of two main components that were developed and released independently, and several other systems that were connected to it. Each component included hundreds of classes and communicated via different interfaces (e. g., RMI, JMS). The customer had about 230 test scenarios in mind, with about 15 steps each. Many steps occurred several times in different scenarios. Some of the systems connected had to be simulated (since they moved containers physically, for example) and these simulators had to be controlled by the automatic tests since they receive and trigger events. Even to build a valid starting point for a scenario was a difficult task that included the initialization of several systems. In the end the test scenarios and test data were defined in several XML files (consisting of 30000 lines). They produced a report in an XML file on each run that took about 10 hours. Test process and test scenarios: An exampleThe test process starts in the analysis phase of the project with the use cases of the system under development - becoming the system under test (SUT) soon. From these use cases the test scenarios for component integration tests are derived. Each test scenario shows that a business requirement is fulfilled. Let's consider a simple order management system. It includes these components:
Each component consists of several classes that store the data and enable the services of the component, e. g., creating a new customer, finding a customer, changing the attributes of a customer, selecting customers that match a search expression. The components are developed by different teams. Each class of the different components is thoroughly tested by JUnit tests. Even the components are tested by JUnit. But who is responsible for the integration test? A test team that should test manually and cannot cope with the flow of major releases, minor releases, bug-fix releases and quick-fixes delivered by the development team. So they test only the parts changed. Let´s see what happens. Some poor guy John sits in the call center using our system, finding that the main use case he requests is not fulfilled:
Each component works for itself fulfilling its requirements - but the process that uses both components does not work any more since one component was changed and nobody checked the overall process - or he checked the process and it worked - but he did not consider the failure of the post code checking service since this failure is difficult to simulate.
Figure 1. Context We will find many variations of this scenario that do differ slightly. e. g., the good one - all works fine; the address is not correct; we select a customer instead of creating him; the customer did order yesterday so we may combine the deliveries; the good ordered is not in stock... For each release of the system each of these scenarios has to be checked - by some poor guy or by automatic tests. Lets choose the automatic tests and let the poor guy derive the test scenarios from the use cases and automate them. Requirements for an integration test frameworkWhat are our requirements for an automatic integration test framework?
Building blocks and interfaces of the test frameworkSimply spoken, iValidator implements a test runner like JUnit. But since integration testing is a little bit more complex than class testing, it defines some more building blocks and interfaces. The building blocks of test scenarios are:
The interfaces of iValidator are used to integrate the framework into different environments:
Developing tests with iValidatorThe major design principle behind iValidator was to enable reuse as much as possible and such reduce costs for testing (btw cost is the main reason for not testing). The main reusable components are:
Units -divide test cases in small, reusable parts. The developer has to write small parts that could be used in different scenarios. Therefore the unit's parameters are separated from the unit´s implementation. The parameters are provided to the unit on test execution. The assembling of units to test cases is done declaratively using XML meta data. (This was true in version 1, now with iValidator version 2 XML is the default repository but it can be replaced by, e. g., a database.) Most integration testing needs a lot of test data as input for the SUT and for control purposes. Data can be defined and grouped within the test description once and used in different test scenarios. For instance, you can reference a whole bunch of data and pull it into the scenario at run time.
Figure 2. Plugin Adapters encapsulate the connection to the components of the SUT (interfaces, database, GUI ...). An adapter has to be developed only once and than can be used in any unit. (Btw, the adapter concept allows you to test any system that could be reached via Java, not only J2EE software.) Roughly speaking, developing automated integration tests with iValidator means to develop units, test data and adapters, and than compose these parts to tests and test suites. It is by no means trivial to build integration tests.It needs a very careful planning and a deep understanding of the problem domain. We strongly recommend to start planning test scenarios when describing the use cases for the system. When you are able to describe what the system should do you are also able to describe how you would verify that the system does what it should. Implementing UnitsOn running tests with iValidator a unit is the smallest item. It is nothing more than a method of a Java class. There are only two requirements, the framework has. First there must be a default public constructor and second the method must not have parameters. Listing 1. AnyClass example
Here you can see that it is easy to use existing code for instance JUnit test classes. But if you want to use the frameworks enhanced features the class in Listing 1 isn't enough. Listing 2. UnitClass.java
Listing 2 shows an enhanced version of the class of Listing 1. This time the class implements the interface Unit. This interface has just one method to give the class access to the framework. A unit is used in the following order:
The interface UnitContext is the central interface between the unit and the framework. In listing 2 there is a simple access to the frameworks logging facilities shown. Using this interface the unit has access to all of the frameworks features. But before we show more of these features we should have a closer look at the XML part and see what is necessary to execute units. The default way to declare the test flow is XML. Listing 3. input.xml file
Listing 3 shows the simplest case of a test
description. There is exactly one test with the name test who runs the
method unitMethod(). The connection comes through the tag
Now we need one more XML file that tells the framework which test description should be executed. Listing 4 shows this. Listing 4. configuration.xml
1: <?xml version="1.0" encoding="UTF-8"?> For this test the files: configuration.xml, input.xml and UnitClass.class have to be stored in the same directory. The iValidator's TestRunner can now be started in this directory issuing the following command: java -jar [install dir]/ivalidator.jar The last dot means, that the configuration.xml in this directory should be used. The output of this test execution is an XML file that will be stored in the default directory. This file looks like Listing 5. Listing 5. Output file
If you now compare iValidator and JUnit you can't see any advantage for iValidator, but keep in mind, iValidator is designed for scenario testing. You will see the power of iValidator soon. From unit to suiteNow imagine you want to reuse units with different sets of parameters. We will show how to build complex scenarios. First we will see how to compose units to suites. Listing 6. Test method
Listing 6 shows two important points. First there
is a tag In a flow there can be any number of tests. Each test can reference a unit or contain a suite. Besides the flow a suite can contain setups, checkups and teardowns. A setup is executed before the flow, checkups and teardowns after the flow.
Figure 3. Run
Typically setups and teardowns are responsible for providing defined system states before and after test execution. Checkups prove conditions after test execution. The framework executes a suite from top to bottom. Later we will see how it handles errors while testing. Listing 7. Complete suite
As you can see in Listing 7, a unit can be used as
setup, test, checkup or teardown. They can be referenced directly via
the ParametersThe iValidator framework gives the user the ability to use parameters in a flexible way. Parameters can be inherited, overwritten or put into libraries. A parameter always consists of a name - value pair and is defined in the test description (Listing 8). Listing 8. Test parameters
At last we show how a unit can access parameters and parameter lists. Therefore it uses the context (Listing 9). Listing 9. Access to parameters
Parameters increase the reuse of units. The different possibilities of defining parameters in the test description allow for a minimal effort on their maintenance. DataThere are limitations on parameters, they are static and they have to be defined prior to test execution. Therefore, iValidator provides the additional concept of test data to handle dynamically created values and to forward data from unit to unit. Data is like parameters built of name - value pairs. Any Java object could be used as value. Units can register, recall and delete data. Listing 10. Unit methods
Listing 10 assumes that unitMethod1 is called before unitMethod2. The method removeData() delivers the data and removes it from the frameworks cache, resetData() deletes all registered data. Errors and flow controlIt is a test framework`s task to find errors but iValidator can not know what is and what isn't an error in the system under test (except RuntimeExceptions). So the unit's developer has to inform the framework about errors. This is done via the context. Currently we distinguish four different error types:
RuntimeExceptions are handled by the framework like severe errors. The frameworks behavior on errors can be configured in every flow. Even so on succesful executed tests. Let's have a look on Listing 11 how the unit tells the framework about success or error. Listing 11. Success or error
The framework has different methods for all four error types. In principle there two different ways to handle errors. The first leads immediately to abortion and needs an exception as parameter. This a feasible method to propagate caught exceptions to the framework giving a hint about the severeness of the exception. The second needs a boolean expression and optionally a description of the error. When the boolean expression evaluates to false the error is reported to the framework. Internally all these methods throw RuntimeExceptions. For instance if the call of context().failure(false) is inside a try/catch block, the error would not propagate to the framework. The developer can control the reaction on errors inside the test description. Listing 12. Error type "failure"
Listing 12 shows an example for the error type „failure“. It means if this error type is raised inside a test in this flow, then the next test should be executed.. The framework knows four different follow up actions:
Next_test_noinits means no setups and no teardowns are executed; next_test_inits would initiate execution of first the teardowns and then the setups of the suite before the next test starts next_flow stops only the actual flow and runs the suite's teardowns, stop leads to a stop of the whole test execution. This kind of control can only be done inside a flow. If errors happen in a setup the whole suite will be stopped. It doesn't make sense to execute the test flow if the setup fails. Only teardowns will be executed to reduce effects on following suites. Errors in checkup and teardown have no influence on the flow's control but on test results. AdaptersTests have to use the interfaces of the system under test and of other systems or simulators. To reduce maintenance costs iValidator uses adapters for encapsulation of the interfaces. When the technical representation of the interface changes, e. g., from RMI to SOAP, only the adapter has to be changed, not the test unit. If the business interface changes, e. g., a new parameter is added, the adapter may provide a default parameter leaving all test units unchanged. Another adapter is written for new test units that may use the new parameter. An adapter can encapsulate whatever the developer wants. Maybe a database access or an interface to some subsystem of the sut. An adapter has to be instanciated once and can be used by any unit (Listing 13). Listing 13. Adapter implementation
The class JndiAdapter shows what has to be implemented to provide a adapter and its factory. You can create an adapter inside a unit like the code in Listing 14: Listing 14. Create an adapter inside a unit
On calling the method getAdapter() the framework first checks if there already is an instance of the adapter needed and, if not, creates a new one using the given factory. For ease of use iValidator has the concept of adapter libraries. Listing 15. The units can reference adapters
As we see in Listing 15 the units can reference adapters from the adapterlib and they do not have to provide the parameters. ReportingReports are written to XML files. The filename consists of a prefix and a timestamp of the beginning of the test execution. For every test the result, start time, end time, a comment and possibly exceptions are reported. The result of a suite comes from the results of all the tests in its flows. Always the worst is the winner. We have the following ranking:
Repositories and ConfigurationThe iValidator framework uses XML as the default for configuration and reporting. But the frameworks architecture is independent of this. The framework can get test descriptions from any source, for instance, a database or a GUI. The same is true for reporting. We call these repositories; iValidator repositories are pluggable. In Listing 4 we showed how to configure iValidator. There you can define the repositories and their properties. What more?There is much more to say about iValidator, have a look at http://www.ivalidator.org/, for instance, parallel test execution, import functions, JMX management, graphical test language, U2TP conformance. ConclusioniValidator has proven to help implementing complex automatic integration tests that are easily maintainable and extensible (even by non-developers, at least to a certain extent). Therefore it supplements JUnit for test scenarios derived from business use cases. The automatic integration tests show that the system under test (still) fulfills the business requirements and drives the business processes, even when changed in unexpected ways. GNU General Public License |
|
| Autor: Guenter Guckelsberger | Nota: |
| Volver | |

