Masaryk University Faculty of Informatics
}w !"#$%&'()+,-./012345<yA|
Automated Testing of the
Component-based Web
Application User Interfaces
Bachelor thesis
Juraj Húska
Hereby I declare, that this paper is my original authorial work, which I have worked out by my own. All sources, references and literature used or excerpted during elaboration of this work are properly cited and listed in complete reference to the due source.
Juraj Húska
I would like to thank RNDr. Petr Ročkai, the advisor of my thesis, for his help, comments and time spent helping me with this work.
I am profoundly grateful to Bc. Lukáš Fryč for his guidance and support throughout my work.
I would like to also thank Mgr. Marek Grác for his advices. My thanks go to my parents, my girlfriend and my close friends as well.
This bachelor thesis explores and describes modern trends in testing enterprise web applications. After the investigation, tools for automated testing of these applications are selected and compared according to maintainability, implementation of these tests and their usage in the systems of continuous integration. Based on the analysis, a high level application programming interface for using in testing the component based web application user interfaces is implemented.
software testing, abstract component model, high-level API, Web UI testing
1 Introduction . . . 1
2 Testing of enterprise web applications . . . 3
2.1 Definition of testing. . . 3
2.2 Division of testing. . . 3
2.2.1 According to way testing is performed . . . 3
Manual testing . . . 3
Automated testing . . . 4
Benefits and drawbacks of both approaches . . . 4
2.2.2 According to depth . . . 6 White-box testing . . . 6 Black-box testing . . . 6 Gray-box testing . . . 6 2.2.3 According to scope . . . 7 Unit testing . . . 7 Integration testing . . . 12 System testing . . . 14 2.3 Continuous integration . . . 17 2.3.1 Key principles . . . 17 2.3.2 Problems . . . 18
2.4 Enterprise applications definition . . . 19
2.5 Enterprise testing approaches . . . 20
2.5.1 Other employed forms of testing . . . 20
2.5.2 Example of enterprise testing process . . . 21
3 Evaluation of the tools . . . 23
3.1 Criteria for choosing the tools examples. . . 23
3.2 Selected tools . . . 23
3.2.1 Arquillian Drone . . . 24
3.2.2 Geb . . . 26
3.3 The results . . . 27
4 Component Model . . . 28
4.1 Motivation for creating the component model . . . 28
4.1.1 Component based Web frameworks . . . 30
4.1.2 Model requirements . . . 31
4.2 Object Oriented UI Testing Patterns . . . 32
4.3 Implementing the Component Model . . . 38
4.3.1 Employed Java technologies . . . 39
Java Reflection . . . 39
Java Dynamic Proxies . . . 41
Java Generics . . . 43
Service Provider Interface . . . 45
4.3.2 Integration with existing Frameworks . . . 45
WebDriver . . . 45
Arquillian Drone . . . 48
4.4 Component Case Studies . . . 52
4.4.1 Autocomplete component . . . 53 4.4.2 Table component . . . 55 4.4.3 Calendar component . . . 58 4.5 Real Deployment . . . 60 4.5.1 Showcase application . . . 60 4.5.2 Functional Tests . . . 61 5 Conclusion . . . 64
A Example of UI test verbosity . . . 70
B Factory class and its methods . . . 71
C Test enricher . . . 73
D Sequence Diagram of Autocomplete Component . . . . 74
E Sequence diagram of Table Component . . . 75
F Sequence Diagram of Calendar Popup Component . . 76
G Omitted Arquillian test configuration. . . . 77
H Manual for building showcase application . . . 78
I Functional test without using of Component Model . . 79
J Functional test with using of Component Model . . . . 82
Testing software is an important part of development of all applications. For enterprise applications, however, it is inevitable. This is due the fact that a development of enterprise applications is costly and time-consuming, but carefully choosing improved testing methods leads to cheaper development and maintenance [1].
At present time, an increasing number of applications, either en-terprise or standard, is focusing on running in browsers, on desktops and mobile devices. This is happening because a web application is easier to distribute. The biggest benefit is that there is no need to create a separate application for each platform. The rule "write once, run everywhere" can be applied.
Testing the functionality of these applications is therefore highly focused on User Interface (UI) testing. One way of reducing the cost of developing and testing applications is automated testing. Present development of scripts for automated UI testing, however, suffers from many drawbacks. The way, in which tests are written, is too on low level and therefore verbose, and also tightly coupled with a specific implementation details of applications. Therefore, often a small change in the application leads to many changes in tests, and thus provides more opportunities to introduce errors into the test. Development and maintenance of such tests is therefore expensive [2].
This thesis is aimed at exploring and describing modern trends in testing enterprise web applications in the first phase, then after the investigation, it is aimed at selecting tools for automated testing of these applications, comparing them according to maintainability, imple-mentation of these tests and their usage in the systems of continuous integration. This analysis should provide high level application pro-gramming interface (API) for use in testing the component based web application user interfaces.
The work’s goal is to improve readability of tests and therefore also the maintainability by employing the created API in testing of enterprise applications.
An API is successfully adapted by users when it provides a clear contract for each component; it should be easily employed; it shall evoke confidence in users that the authors are able to maintain the API, and
should build on already well-known projects. These are the objectives that the API focuses on, as these are also the points that are considered when deploying a new dependency in enterprise solutions.
The second chapter of thesis analyses the different types of testing enterprise applications, and the third chapter compares selected tools for automating UI testing. Integration and implementation of the created API is described in Chapter 4. This chapter also describes the selection of three major components, for which I created the API and their implementation and their use in real-life example. Benefits of using the created API are described in chapter 4 as well.
2.1
Definition of testing
Testing software is any activity aimed at evaluating an attribute or capability of a program and determining that it meets its required results [3].
Testing approach depends on the software development life cycle model1, and thus it needs to fit the model being used for the tested
project. However, a common objective of all approaches is to find the problem as soon as possible, to reduce the cost of fixing it later [4].
2.2
Division of testing
The attributes of testing, like how big pieces of AUT (application under test) we are testing, or what knowledge about AUT the tester2 has, and the way testing is performed, divides testing activities into several groups of types.
2.2.1 According to way testing is performed
Test can be executed manually by humans or automatically by comput-ers.
Manual testing
It involves manual tasks, such as: setting up the test environment, executing the tested functionality, collecting and reviewing the results and recording found issues.
This process can be done by following a test plan or, alternatively, by exploratory testing [5].
Following a test plan Test plan is a formal list of steps needed to be done to test an application functionality. According to IEE
829-1. The process of creating a software product from its initial conception to its release [4].
2008 [6] it should contain among other things: a test plan identifier, an introduction, test items, the features to be tested, the features not to be tested, an approach, item pass/fail criteria, and other prerequisites.
Because it uses a predefined plan to test an application, it does not depend tightly on tester’s skills. On the other hand, it can be more time consuming to create and maintain test plans.
Exploratory testing It is not tied with any test plan, hence requires less time for preparation, and can discover important problems quickly.
The problems are that it is harder to reproduce any discovered problems in later application development phases, and exploratory testing also tightly depends on tester skills.
Automated testing
Execution of tests without human intervention can be defined as au-tomated testing. Auau-tomated tests execution includes, however, other prerequisites [7]:
• Ability to run a subset of all tests.
• Automatic set-up and record environmental variables.
• Running the test cases.
• Capturing the results.
• Comparing actual and expected results and highlighting the differences.
• Analysing the results and processing them in a comprehensive and clear way.
Benefits and drawbacks of both approaches
Both manual and automated testing approaches have benefits as well as drawbacks. An application context should help with deciding which one is more suitable. The context includes following: how big the application is, how big is the budget of the project, or when it should be released.
Manual testing benefits The set-up time is shorter in comparison to automated testing. Moreover, it is possible to easily test manually during all development phases, since there are no constraints on what is suitable to test manually and what is not.
Manual testing drawbacks Repetitive and mundane tasks per-formed during manual testing can lead to inaccurate or incorrect results. The execution of tests is also slower than tests executed by a computer.
Automated testing benefits and drawbacks Automated testing has benefits for bigger projects, since the initial cost for automation, and test maintenance can be high. Some of the benefits and simultaneously drawbacks are [1]:
• Automation helps to eliminate human errors - some of the mis-takes which are made during manual testing can be reduced. This concerns errors which were incurred by performing a long list of mundane activities.
• Automated tests are faster than manual testing - this is consid-ering only the actual execution of tests, it does not tell anything about reviewing the results and then possibly fixing the broken test. Because the execution is faster, the results are accumulated faster too, hence the tester needs to review more results in a shorter amount of time.
• Automation can lead to cost reductions.
To claim that automation can reduce costs, we need to firstly realize, what is included to overall automation costs [1]:
• The cost of implementing a test harness3.
• The cost of learning how to use the harness.
• The cost of developing automated tests.
• The cost of maintaining the tests as the product change.
• The cost of reviewing the test results.
This overall cost needs to be added to the cost of the remaining manual testing costs, as some manual testing is still likely to be required, especially when testing UI. This sum need to be compared to the sum of manual testing only.
2.2.2 According to depth
Next chosen division is according to the attribute which reflects the depth of tester insight into the tested application [4]:
White-box testing
It is also known as clear box testing, and denotes technique, when tester knows the underlying implementation of an application, so he can decide how to develop the tests according to this knowledge. It is used mainly for checking small parts of an application, for example their database transactions.
Black-box testing
It indicates the way of testing, where the tester does not know the details of how the AUT works. An output of the tested action is important, but not how the action was done. It is used mainly for testing of bigger, already implemented parts of the AUT functionality.
Gray-box testing
It has both black-box and white-box testing characteristics. An example of this is testing of functionality of web applications, because to perform this type of testing, the knowledge of web page structure is required, and simultaneously it is not important how the tested functionality was performed.
2.2.3 According to scope
The last4 described attribute is a scope [8] of the testing. The scope denotes the size of the tested parts, or more specifically, indicates the number and the size of the parts, needed to achieve a tested functionality. It also stands for the development cycle5 of the application it is currently in, when the tests are written for it.
The motivation behind separating pieces which should be tested, is that testing pieces in isolation allows identification of core fault easily. Testing pieces of the application would lead to avoiding many sur-prises when testing the entire product. In other words, when the fault is found in an unit, then probably that unit is defective, when the fault is occurring in two integrated units, it has to be related to how units interact [4].
Unit testing
A unit is the smallest testable piece of software which can be compiled and put under test harness [10].
Unit testing (sometimes module testing [4]) is typically performed as white-box testing (see section 2.1), because the tester needs to know the code which he is going to test [11].
Scope of unit testing is narrow, in other words, it should verify small parts of the entire application. An example of unit testing can be testing of an unit which is responsible for computing factorial of a natural number.
Unit tests are usually written by developers of the application, or framework. The tests should be developed and executed in prior to or during the implementation of the modules [11]. It is not necessary to wait with testing of units until all units are implemented because they should not have dependencies on each other.
4. There are more attributes for dividing testing methods into groups, see [8] for basic overview.
5. For example linear development methodology called Waterfall recognizes these development phases:Initial Investigation, Requirements Definition, System Design, Coding and Testing, Implementation, Operation and Support[9].
Formally it is standardized by, for example, IEEE Standard for Software Unit Testing6, which describes the exact process which should
be performed, its inputs and outputs.
When comparing unit testing with testing of bigger parts of AUT, unit tests are often easier to develop, as they are more straightfor-ward, and have less dependencies. Also running unit tests is less time-consuming than running other types of tests.
The drawbacks come from the fact that with unit testing, we cannot verify that an application is entirely flawless, even when we will be testing all units. The problem is, that they are tested separately, and after they will be integrated, some critical issues can arise.
Writing unit tests is laborious, cumbersome and often difficult task. It is because the testing code is written at low level of abstraction, therefore, it can be tedious to introduce a change in tests to match the code under test changes [12]. Hence complete automation of unit testing is not only feasible but also effective [13].
There are several tools which leverage unit testing and provide integration with IDE (Integrated Development Environment)7. In Java
environment, the most used frameworks are JUnit8 and TestNG9, which provide almost the same functionality nowadays, mainly the ability to annotate a method with proper annotation to mark which method is a test method, and then collecting results in some standard format, to easily interpret them with IDE.
Mock versus real objects testing "Not all code is self contained" [16], in other words, real applications have a lot of dependencies. Unit tests are intended to test as much independent modules as possible. This is, however, very hard to achieve in real world applications.10
The tester then has basically three options:
• test with real objects;
6. ANSI/IEEE Std 1008-1987, http://standards.ieee.org/findstds/
standard/1008-1987.html.
7. An application which provides environment for development, debugging and more. An example is Eclipse [14] or NetBeans [15].
8. JUnit,http://www.junit.org/.
9. TestNG,http://testng.org/doc/index.html.
10. Applications not created with purpose of learning some technology, but to have real addition.
• test with mock objects;
• test with stubs.
Real objects Testing with real objects would mean to use some of the dependencies the tested unit has. But this means relying on the fact that used dependencies are correct, that they are tested somewhere else [16]. Suppose the listing 2.1.
1 import j a v a . u t i l . L i s t ; 2 import j a v a . u t i l . A r r a y L i s t ; 3 4 import o r g . my . f o o . Equipment ; 5 6 public c l a s s Garage { 7 8 // w i l l r e t u r n f a l s e i f t h e r e was c o r r u p t e d t o o l .
9 public boolean c h e c k T o o l s C o n d i t i o n ( L i s t <Equipment> t o o l s
) { 10 11 i n t numberOfTools = t o o l s . s i z e ( ) ; 12 13 f o r( i n t i = 0 ; i < numberOfTools ; i ++) { 14 boolean good = ( t o o l s . g e t ( i ) ) . c h e c k C o n d i t i o n ( ) ; 15 16 i f( ! good ) { 17 return f a l s e; 18 } 19 } 20 21 return true; 22 } 23 }
Listing 2.1: Example of using third party dependency to implement our service.
On lines 9, 11 and 12, our artificial11 example depends on a third party service, provided by imported dependencies of classes Listand
ArrayList. When testing this service (Listing 2.2, line 17), we will be also testing that these dependencies are working properly[16].
1 import j a v a . u t i l . L i s t ; 2 import j a v a . u t i l . A r r a y L i s t ; 3 4 import o r g . my . f o o . Equipment ; 5 6 public c l a s s TestGarage { 7 8 @Test 9 public void t e s t C h e c k T o o l s C o n d i t i o n ( ) { 10 Equipment axe = new Equipment (" axe ") ; 11 axe . s e t C o n d i t i o n ( ConditionEnum .BAD) ; 12
13 L i s t <Equipment> t o o l s = new A r r a y L i s t <Equipment >() ; 14 t o o l s . add ( axe ) ;
15
16 Garage g a r a g e = new Garage ( ) ;
17 boolean r e s u l t = g a r a g e . c h e c k T o o l s C o n d i t i o n ( t o o l s ) ; 18 19 a s s e r t F a l s e ( r e s u l t , " The method d i d n o t d i s c o v e r c o r r u p t e d t o o l ! ") ; 20 } 21 }
Listing 2.2: Example of testing with real objects.
And that was not our intention. since we wanted to test only Garage
behaviours, isolated. Indeed, this is only an contrived example, but in real world applications, reliance on the correctness of third party depen-dencies can lead to bugs and unit test failures caused by malfunction not of the Garage class itself. This kind of problems is often hard to find and reproduce, and therefore should be avoided somehow.
Mock objects One of the solutions for this is to use mock objects instead of real objects.
Mock objects encourage testers to write better structured tests with reduced dependencies. It is a technique used in TDD (Test-Driven-Development12) [18].
In other words, tester will mock the services provided by other than tested class. He will define the exact behaviour the services should have,
12. Software development methodology which among the other things, encourage to write tests at first, then implementation [17].
and by this, he does not suppose that the services works as expected any more. It does not have to be used only in TDD, but in other testing methodologies as well.
In Java environment, there are several mocking frameworks. Listing 2.3 demonstrates creation of a mock object in Mockito13for dependencies
from Listing 2.2. 1 import j a v a . u t i l . L i s t ; 2 import j a v a . u t i l . A r r a y L i s t ; 3 4 import o r g . my . f o o . Equipment ; 5 6 public c l a s s TestGarage { 7 8 @Test 9 public void t e s t C h e c k T o o l s C o n d i t i o n ( ) { 10 Equipment axe = new Equipment (" axe ") ; 11 axe . s e t C o n d i t i o n ( ConditionEnum .BAD) ; 12
13 L i s t <Equipment> t o o l s = Mockito . mock ( L i s t .c l a s s) ; 14 when ( t o o l s . g e t ( a n y I n t ( ) ) ) . t h e n R e t u r n ( axe ) ;
15 when ( t o o l s . s i z e ( ) ) . t h e n R e t u r n ( 1 ) ; 16
17 Garage g a r a g e = new Garage ( ) ;
18 boolean r e s u l t = g a r a g e . c h e c k T o o l s C o n d i t i o n ( t o o l s ) ; 19 20 a s s e r t F a l s e ( r e s u l t , " The method d i d n o t d i s c o v e r c o r r u p t e d t o o l ! ") ; 21 } 22 }
Listing 2.3: Example of creating Mock object for third party dependencies.
According to [18], after a revision of the initial definitions of Mock objects, testers are encouraged to mock only dependencies they own. In our example we should mock the Equipment class instead of List
interface.14
13. Mockito - Java Mock framework, more info at http://code.google.com/p/ mockito/.
Stubs Another solution for testing units without their external dependencies is to replace these dependencies by stubs which are real objects developed against defined interface to substitute functionality of real implementation.
The difference between stubs and mock objects is that stubs need to have predefined behaviour, while behaviour of mock objects is defined at runtime.1516.
Integration testing
Next level after testing units, is testing of integrated units, so-called integration testing (sometimes product testing [11]).
These units are considered to be flawless, as their unit tests are passing without problems, and we want to verify that aggregating of these components cannot create a problem. An example of such problems can be incorrect return value of a method call, or insufficient validation criteria [10].
A special kind of integration testing is application programming interface17 (API) testing. It is used, for example, to validate REST18 API, or an API of web components, which is done for this thesis.
Integration testing can be performed as both white-box and black-box testing (see section 2.2.2). It should be performed by an independent test team, and can begin as soon as enough of the tested units are implemented, or when units need to integrate with components outside of system, for instance with database.
During this phase, all customer environment variations should be considered and tested [11]. Meaning that, for example, all supported Operating Systems or browsers should be combined and tested.
Automation of integration testing is even more difficult than unit testing automation. It is because of integration-level complexity, which is firstly caused by complicated initial system set-up. To test integration
15. More information about differences between stubs and mock objects can be found athttp://martinfowler.com/articles/mocksArentStubs.html.
16. An example of a stub created for this thesis isAbstractComponentStub, which can be found in theapimodule.
17. API, a specification intended to be used as an interface for communicating among software components [19].
issues whole system need to be set to an initial state. Secondly it is caused by problematic retrieving of tests results, suppose for example retrieving results from testing of database transactions [20].
The challenge for web applications is that they often need to run in an application container in order to work. The application container, used mainly in Java EE19, is an application which provides various
services for the AUT, such as handling concurrent access to the applica-tion, management of application’s dependencies, or providing database connections [21].
In such environment, there are application dependencies and services which are often provided by a container. To provide this functionality for tests, a tester has basically two options [22]:
• mock the application dependencies provided by a container as described in subsection 2.2.3;
• or to deploy tests into container, and performin-container testing.
Mock integration environment Using Mock objects brings an un-certainty to our tests, because we cannot be sure that the application will behave the same in the real environment, in this case, in the container. To avoid this uncertainty, in-container testing can be employed.
On the other side, mocks allow us to run tests faster than in-container, since they do not have to take unnecessary actions like parsing configu-ration of the application.
In container testing This type of testing involves deploying our tests to the container, execute them, and collect the results remotely. It is also a typical example of gray-box testing (section 2.2.2), because application container provides a logic which implementation is unknown.
Although this brings a significant advantage of having the depen-dencies being provided by the container, it also brings another layer to our testing infrastructure [22]. This layer represents a lot of new code lines, or used dependencies which would manage shutting down the application container, packaging and deploying the AUT, executing tests and other.
19. Java Enterprise Edition, http://www.oracle.com/technetwork/java/ javaee/overview/index.html.
Hence, in-container testing pose problems with test readability and maintenance, since the tester is not responsible for underlying imple-mentation.
System testing
Several units aggregated into one make a component; several components integrated into one make one big component, called system.
Testing of such big component is called system testing [10]. It is aimed to reveal problems which can be exposed by only testing the entire integrated system or a major part of it. An example can be an error exposed by different communication network protocol, used for communicating with other components or systems [11], or in case of web applications, testing of user interfaces.
It is an example of black-box testing (section 2.2.2) and it is the third phase according to target testing, during which the test team should replicate application user environment. It should begin after all components functionality was implemented.
Functional Testing It is similar to system testing, because it is a kind of black-box testing (see the section 2.1), where the user point of view is taken into account. Meaning that, the application is subjected to various inputs, and its outputs are the subject to testing, because they need to conform with specified behaviour [10].
The performance of functional testing on a web application, would mean that a tester should verify functionality of all pages, all components against various inputs. A simple example can be verification that, clicking on the button would cause expected action.
Because testing of the web page functionality relates to testing of the UI (User Interface), it has the same properties, both advantages and disadvantages. One of the advantages is that it is quite clear what to test and how to test, therefore it can be performed by less technically trained people.
Automation UI testing problems Testing of User Interface has some specifics. One of them is large basis of possible inputs, and interactions which an user can perform with a web page.
Manual testing is, therefore, quite error-prone, as a tester can easily forget to test some of the functionality from the specification, as he is doing a repetitive task. It is also very time consuming, therefore expensive. One of the solutions is exploratory testing (section 2.2.1).
These are the reasons that it is a good practice to have parts of the functional testing of the web applications automatized. As section 2.2.1 described, one of the requirements to have tests automatized, is to have defined a script which will instruct the particular testing driver, what interactions with a tested web application need to be done. By this, the execution of the tests can be repeated.
This script can be assembled in two ways, usually by a tool integrated with a browser. Tool captures user interactions with the web application, therefore, they can be repeated later. Second way is to define the script programmatically, which means writing it with use of calling methods from a testing tool API.
An example of the programmatic script is in Listing 2.4.
1 public c l a s s T e s t N a v i g a t i o n O v e r P a g e s { 2 3 private WebDriver d r i v e r ; 4 5 @Test 6 public void t e s t P o p u p { 7 d r i v e r = new F i r e f o x D r i v e r ( ) ; 8 9 d r i v e r . g e t 10 (" h t t p : / / l o c a l h o s t : 8 0 8 0 / myTestApp/ i n d e x . html ") ; 11 12 d r i v e r . f i n d E l e m e n t ( By . i d (" b u t t o n ") ) . c l i c k ( ) ; 13 14 WebElement popup = d r i v e r . f i n d E l e m e n t 15 ( By . xpath (" //∗span [ @id=’popup ’ ] ") ) ; 16
17 a s s e r t T r u e ( popup . i s D i s p l a y e d ( ) , " The popup s h o u l d be d i s p l a y e d , when c l i c k i n g on t h e b u t t o n ! ") ;
18 } 19 }
Listing 2.4: Example of programmatic script for testing simple web application page.
When considering the Listing 2.4, it is noticeable that it exposes some of the programmatic UI testing deficiencies20, such as:
• Tests maintenance;
• Inability to test every aspect of the application
• Reliability of tests
• Non-effectiveness for some agile development methods
• Re-usability of the tests
• Slow speed of test execution
Tests maintenance- there is a hard-coded structure of tested web page, on the lines 11 and 13. Should this structure change in the future, the test will need to be changed as well. When considering 10,000 tests for an application, this low level approach would mean increased costs for tests maintenance. The best approach would be if the test class could be abstracted from this structure.
Another problem is using of simple String objects, as a method parameters, instead of type-safe21 way, which would include usage of
objects other than String, and therefore reflecting on their purpose. This was just a very simple example of test, real UI tests tend to be very verbose (see the appendix A)
These problems are more noticeable when maintaining several tests, especially for an application which evolves.
Inability to test every aspect of the application - the script is just testing that after clicking on the button, a pop-up is displayed. Whether the pop-up was rendered correctly, that is, whether it is in the correct place, with correct skin, and with a proper size would be very difficult programmatically. Therefore, some of the web application aspects still need to be tested manually.22
20. The deficiencies are not caused by tool selecting, they are very common for all such tools.
21. It is a way of forcing a developer to use some particular type of the object, to ensure discovery of type errors at compile time.
22. There are currently some tools which leverage some of the visual testing burdens, but they can not still fully replace a human.
Reliability of tests - tools which use JavaScript browser engine to instruct browser what interactions are need to be done for particular test, are quite unreliable. Each browser has different JavaScript engine, and testing on various browsers means writing browser-specific code for some tests. This is, again, another load for test maintenance.
Non-effectiveness of some agile development methods - the fact, that tests hold information about implementation structure, causes that they need to be written after the implementation of tested func-tionality. Hence, they are quite unusable with agile techniques like TDD [23].
Re-usability of the tests - many parts of the tests are repeated in other tests, because tested functionality requires common actions to be done before the actual testing.
Tests tend to be slow to execute - the execution of simple test can be measured in seconds. When running the whole test suite, it can be increased to days to finish. This is quite a big problem in Continuous Integration (CI) (see section 2.3) environment.
2.3
Continuous integration
Continuous integration has its roots in XP (Extreme Programming), which is agile23 software development methodology, aimed to improve
software quality, while reducing time needed to respond to customer’s constantly changing demands [24].
The software quality is improved by reducing the integration (see the section 2.2.3) risk [25]. That is the risk of testing on the integration level in the end of the product life cycle development, instead of testing continuously to discover and fix possible errors as soon as possible. Therefore, integration testing is feasible not only for integration testing, but as well as for unit testing and system testing.
2.3.1 Key principles
To achieve better software quality, CI process has to duly observe following key principles [25]:
23. Agile software development methods using practices of incremental a iterative development.
• Application code should be maintained in the code repository.
• Build and tests of the application should be automated.
• Code changes should be delivered to the code base every day.
• Each delivering of the code should trigger project build and testing process on dedicated machine.
• The building and testing should be as fast as possible.
• Test should be run in the production environment.
• Results should be clearly visible and authors of the code delivery should be notified that their code delivery might cause failure.
• The latest version of application should be easily accessible, and this process of releasing the application should be automatized.
2.3.2 Problems
The benefits which CI brings outweigh the disadvantages, but they need to be considered as well. The most critical are:
• Initial investments into hardware, that is usually dedicated ma-chine with CI tool running on it and its slaves on which the actual CI build and testing is performed. The cost could be partially limited by virtualization of some of the machines.24
• Security vulnerabilities need to be taken into account, because an attack to CI system can mean disclosure of confidential infor-mation or system shutdown, which can have a negative impact on application development process.
• Tests need to be automatized, which can extensively increase the overall development expenses.
24. Virtualization is a creation of virtual hardware platform, or operating system, etc.
2.4
Enterprise applications definition
Enterprise applications are software products designed to facilitate co-operation and coordination of work across the enterprise. It includes the interconnection of core business processes like sales, accounting, finance, human resources, and other. Very often, it also provides interfaces to connect customers, suppliers and other business partners to that system [26].
The more functionality they provide, the more complex they become. There is also stress on proper development process to avoid unnecessary costs and to provide product with sufficient quality.
As they often perform work with important data, which need to be processed in real time, the focus is on other important aspects, and challenges as well, like namely [27]:
• Performance;
• Data persistence;
• Accessing data concurrently;
• Numerous UI screens;
• Integration with other enterprise applications;
• Security.
Performance - is important for all kinds of applications but for enter-prise software, it is often crucial, as their performance can significantly influence the usability and the responsiveness to stimuli. These perfor-mance deficiencies may have an impact on customers, therefore, can cause significant monetary loss.
Data persistence - enterprise applications works with a lot of data which need to persist not only between multiple runs of the application but several years. The system has to be adaptable to the changes of the data structure as new requirements for the new system functionality emerge.
Accessing data concurrently - especially for Web enterprise systems, there is a large number of users, who access the system concurrently.
Therefore, there has to be reliable mechanism to ensure, that this concurrent access is not changing the data in a way that causes errors.
Numerous UI screens - a lot of data means a lot of distinct UI screens that handle this data. They need to be presented in many various ways, for all kinds of users, which can bring a lot of error-prone lines of code. Integration with other enterprise applications - enterprise applica-tions need to collaborate with other applicaapplica-tions. The problem is, that these applications are often built to run in different environments, writ-ten in other programming languages. This, again, introduces a lot of error-prone places in the application code.
Security - because enterprise applications control money and re-sources, any security violation can cause, for instance, loss of confidential or classified data, or other significant financial loss [28].
2.5
Enterprise testing approaches
In addition to forms of testing described so far, not only the enterprise software employs other important forms of testing [29].
2.5.1 Other employed forms of testing
• The first described is Alpha testing. It is performed by the inter-nal development team or quality assurance team in the controlled environment, typically dedicated labs with an environment simu-lating customers requirements. It is aimed to discover deployment and workload issues, which need to be captured before the final release.
• Acceptance testing is utilized for customized software and ap-plications, designed for particular customer, rather than general applications for broader base of users. The customer chooses a user who will validate the application according to its specifica-tion.
• Installation testing validates compatibility with hardware plat-forms, that is, whether the application is portable to different hardware and software platforms if this is requested by the cus-tomer. The portability is the ability to run the application on
different hardware and software platforms without additional changes required from the end user of application.
• Stress testing is used to simulate abnormal behaviour in a pro-gram by considering situations that cause abnormal end of the program, to create a benchmark of software stability.
• Smoke testing is aimed to test the code after the introduction of a new code, to confirm that new changes do not compromise the integrity of the product. Smoke tests should be fast to execute, because they are often used to quickly validate the integrity of the product, before deeper testing takes place.
• Usability testing measures the simplicity of using the application. In case of applications with GUI (Graphical User Interface), these interfaces are the subject of examination measuring for example user accuracy or application response times.
2.5.2 Example of enterprise testing process
To meet the requirements from section 2.4, enterprise software needs to be tested in a proper way.
To test the application properly, there is a need to test every feature of the product on every possible configuration [11].
This can be, however, very exhausting process. Therefore, there is a demand for efficient testing. We need to find an approach which enables finding most of the problems, for as reasonable cost as possible [11].
Hence, a big emphasis is put on the automated testing. In other words, what can be automated is automated, the rest is done manually. CI is another employed technique, into which a lot of funds is invested. It is usually the most important part of the testing structure, thus is critical for enterprise products.
Figure 2.1 shows an example of enterprise testing process time-line [11] [29]. First Milestone is a term for first development milestone, when the application is in the state, when the system testing can begin. Code freeze is the state when no additional code changes are allowed.
3.1
Criteria for choosing the tools examples
The aim was to choose several tools for automated testing of the en-terprise web applications and consequently compare them according to maintainability, implementation of these tests and their usage in the systems of continuous integration.
From the variety of web UI automation testing tools [30], I choose and concentrated mainly on tools which are suitable for enterprise usage. That means, they have to provide cross-browser testing ability. In other words, they have to support all major browsers, which are currently Firefox, Internet Explorer, Safari, and Google Chrome1, on the major
Operating Systems platforms (Linux, Windows, Mac).
The selected tools have to be able to provide programmatic way of creating test scenarios as we believe, this would be the way to enable further maintainability improvements and CI integration. Test automation is very similar to software development, therefore, the same logic can be applied [2].
Next requirement was that the selected tool has to be open sourced2,
which allows use of this tool as a base for the created component model. The tool has to be enabled for testing rich web applications. In other words, it has to support testing of Ajax-based web sites.
I focused on projects with well based community behind them, as enterprise solutions usually cannot afford to invest money into unstable or small-scale projects. The best way was to choose project with a financial supporter behind it, as for example a big software company which either employs the framework developers or provides other support for it.
3.2
Selected tools
According to criteria for selecting from section 3.1, I chose following frameworks:
1. See reference [31].
• Arquillian Drone [32]
• Geb [33]
3.2.1 Arquillian Drone
Arquillian is a testing platform that enables developers to create au-tomated integration, functional and acceptance tests. Its main focus is on separating the test and the runtime, so a tester can concentrate on test logic rather than on managing the runtime from the test. This brings lot of benefits for an in-container testing 2.2.3, while Arquillian is eliminating the burden connected with it. Among other things, it manages following:
• The life cycle of the application container.
• Packages test cases and other dependencies into an archive.
• Deploys this archive to the containers.
• Enriches the test case by Dependency Injection (DI).3
• Executes the test inside or against the container.
• Collects the results remotely and returns them for reporting. I chose Arquillian extension called Drone. It inherits all aspects of Arquillian, and furthermore adds support for managing life cycle of browser.
The list of supported browsers begins as it is required with the major used browsers. Since Arquillian was created to be highly extendable it also enables to add support of any desired browser.
Drone is not a driver itself, it integrates with existing drivers, such as Graphene [34] and automation tool Selenium 24 which is a union of Selenium 1 and project WebDriver5.
Graphene is a wrapper for Selenium, that was created to provide type-safe way of using the Selenium API, and other tools for testing an
3. DI,http://martinfowler.com/articles/injection.html.
4. Selenium 2,http://seleniumhq.org/.
Ajax enabled web pages. An Arquillian project is configured via Apache Maven6 build system.
An example of a test written in Arquillian Graphene for an artificial application, can be found in Listing 3.1.
It is shortened, as for example Java imports statements are left to keep it readable. Also the method for deployment of AUT is left. The example of this deployment method can be found in appendix G, where AUT is assembled with the use of ShrinkWrap7 project.
In following listing, line 1 indicates that it is an Arquillian test, line 3 and 4 is DI for context root, that is, the root part of the URL which will be accessed in order to load the application in the particular browser.
Line 6 and 7 injects the driver which is in this example Arquillian Graphene, via which the browser is instructed.
On the lines 9-25 a test is declared that simply fills some characters in the input, triggers an Ajax action and asserts that the generated output is the same as the inserted characters.
1 public c l a s s TestMyApp extends A r q u i l l i a n { 2 3 @ A r q u i l l i a n R e s o u r c e 4 protected URL c o n t e x t R o o t ; 5 6 @Drone 7 protected A j a x S e l e n i u m s e l e n i u m ; 8 9 @Test 10 public void t e s t A j a x I n p u t ( ) { 11 12 s e l e n i u m . open ( URLUtils . b u i l d U r l ( c o n t e x t R o o t , " /myApp/ i n t e x . html ") ) ; 13 14 S t r i n g t e s t S t r i n g = " T e s t S t r i n g "; 15 16 J Que ry Lo ca to r i n p u t = j q (" . myInput ") ; 17 18 s e l e n i u m . typeKeys ( i n p u t , t e s t S t r i n g ) ;
19 guardXhr ( s e l e n i u m ) . f i r e E v e n t ( i n p u t , Event .KEYUP) ; 20
21 J Que ry Lo ca to r ajaxOutput = j q (" . myOutput ") ;
6. Apache Maven,http://maven.apache.org/. 7. ShrinkWrap, http://www.jboss.org/shrinkwrap.
22 23 S t r i n g a c t u a l O u t p u t = s e l e n i u m . g e t T e x t ( ajaxOutput ) . t r i m ( ) ; 24 a s s e r t E q u a l s ( a c t u a l O u t p u t , t e s t S t r i n g , " The o u t p u t g e n e r a t e d by Ajax i s n o t c o r r e c t ! ") ; 25 } 26 }
Listing 3.1: Example of test written in Arquillian Graphene.
3.2.2 Geb
Geb is the second chosen tool. It also uses WebDriver internally together with JQuery8 content selection. It is similar to Arquillian Drone in terms of cross-browser testing and ability to test asynchronous pages.
The differences from Arquillian Drone is that the test scenarios are written in Groovy. And it also lacks the Arquillian support for management of test life cycle. Apache Maven can be used for building. Support for Gradle9 and Grails.10 is added.
An example of test written in Geb is shown in Listing 3.2. The test scenario is the same as in listing 3.1.
On line 2, it is accessing the URL on which the tested application is deployed, line 4 ensures that the tested page was completely loaded.
On line 6, it is using JQuery like syntax for locating of element with class myInput, and consequently the found element is filled with value
Test String.
Line 8 asserts that the element with class myOutput was updated with expected value.
8. JQuery,http://jquery.com/. 9. Gradle,http://www.gradle.org/. 10. Grails,http://grails.org/.
1 Browser . d r i v e { 2 go " h t t p : / / l o c a l h o s t : 8 0 8 0 /myApp/ i n t e x . html " 3 4 a s s e r t t i t l e == "MyApp" 5 6 $ (" i n p u t " , c l a s s: " myInput ") . v a l u e (" T e s t S t r i n g ") 7 8 w a i t F o r { $ (" d i v " , c l a s s: " myOutput ") . t e x t ( ) == " T e s t S t r i n g " } 9 }
Listing 3.2: Example of test written in Geb
3.3
The results
After the evaluation I would like to single out Arquillian Drone. There are several reasons for it. The first would be the Arquillian container management support which gives us a great tool to use in CI. Geb is also supported in CI. However, lot of functionality has to be implemented by the user to obtain the same result as with Arquillian. Arquillian is also easily extendable which gives us an opportunity to integrate our project with Arquillian Graphene later on. It also supports so called extensions which will help us to enhance usability of our created API.
When considering the listings 3.1 and 3.2, at first glance it is notice-able that Geb example is less verbose, however, it does not provide the users with the functionality that Arquillian Graphene does, for instance the way how Graphene ensures that an Ajax event was fired (line 19). Another advantage of Arquillian Graphene tests is its type-safety. Possible flaws of code lines in JQuery syntax can be only found at runtime. This makes Arquillian Graphene tests more robust and there-fore, more suitable for enterprise environment. It also enables the users to encapsulate the components in more readable way, hence better maintainable way, which will be useful later in API creation.
A component is a basic unit of the component-based Web frameworks (section 4.1.1). That is, for instance a calendar component. A graphical representation of the calendar component can be found in the figure 4.1.
The term component model is used for the purpose of this thesis as a term for referencing an abstract API an its implementation, that was created for this thesis.
Figure 4.1: Calendar component - RichFaces implementation.
4.1
Motivation for creating the component model
Users of RichFaces framework (section 4.1.1) demand a way to easily test their enterprise applications. A component model was created to help them. The model encapsulates behaviour of all RichFaces components. These components will be then used in functional tests, to enhance tests maintainability, by encapsulating the structure of HTML code for particular component at one place. This way we could avoid code repetition and incidental details [2], which are the main inhibitors to introduce a change in the software.
The idea was, instead of writing tests like demonstrated in Listing 4.2, write tests similar to the test demonstrated in Listing 4.1. Both tests on listings verify simple test scenario, where setting the current
date to the calendar will be correctly reflected in the input for that calendar.
Note the differences: whereas in the first example the structure of the web page is hard-coded in the test, the second example uses services of calendar component which encapsulates that structure to achieve the same functionality. This way it is more visible what the purpose of test is. Furthermore, the more readable the code is, the better maintainable it is.
1 public c l a s s T e s t C a l e n d a r { 2
3 @FindBy ( xpath = " // d i v [ @id=’ r o o t E l e m e n t ’ ] ") 4 R i c h F a c e s C a l e n d a r c a l e n d a r = new
R i c h F a c e s C a l e n d a r (" l o c a t o r D e t e r m i n i n g C a l e n d a r ") ;
5
6 @Test
7 public void t e s t A j a x I n p u t ( ) {
8 Date e x p e c t e d D a t e = new Date ( ) ; 9 10 c a l e n d a r . show ( ) ; 11 c a l e n d a r . setToday ( ) ; 12 13 Date a c t u a l S e t D a t e = c a l e n d a r . g e t S e t D a t e ( ) ; 14 15 checkThatDatesAreSame ( e x p e c t e d D a t e , a c t u a l S e t D a t e ) ; 16 } 17 }
Listing 4.1: Test for RichFaces calendar Arquillian, using component model. 1 public c l a s s T e s t C a l e n d a r { 2 3 protected JQ ue ry Lo ca to r imgWhichInvokesTheCalendar = j q (" img . r f−c a l−btn ") ; 4 protected JQ ue ry Lo ca to r a p p l y B u t t o n = j q (" d i v [ o n c l i c k∗= c l o s e ] : c o n t a i n s ( ’ Apply ’ ) ") ; 5 protected JQ ue ry Lo ca to r todayButton = j q (" d i v [ o n c l i c k∗=today ] : v i s i b l e ") ; 6 protected JQ ue ry Lo ca to r c a l e n d a r I n p u t = j q (" . r f−c a l−i n p ") ; 7 8 @Test
9 public void t e s t A j a x I n p u t ( ) {
10 s e l e n i u m . c l i c k ( imgWhichInvokesTheCalendar ) ; 11
12 waitGui . f a i l W i t h (new RuntimeException (" C a l e n d a r was n o t opened ! ") ) 13 . t i m e o u t ( 1 0 0 0 ) 14 . u n t i l ( e l e m e n t V i s i b l e . l o c a t o r ( todayButton ) ) ; 15 16 s e l e n i u m . c l i c k ( todayButton ) ; 17 s e l e n i u m . c l i c k ( a p p l y B u t t o n ) ; 18
19 waitGui . f a i l W i t h (new RuntimeException (" C a l e n d a r was n o t c l o s e d ! ") )
20 . t i m e o u t ( 1 0 0 0 )
21 . u n t i l ( e l e m e n t N o t V i s i b l e . l o c a t o r ( todayButton ) ) ; 22
23 Date e x p e c t e d D a t e = new Date ( ) ; 24 Date a c t u a l S e t D a t e = parseDateFromInput ( c a l e n d a r I n p u t ) ; 25 26 checkThatDatesAreSame ( e x p e c t e d D a t e , a c t u a l S e t D a t e ) ; 27 } 28 }
Listing 4.2: Test for RichFaces calendar in Arquillian Graphene. Moreover this API does not have to reflect only on specific implemen-tation of UI components, it can be transformed to provide unified API for various UI component libraries. These libraries can be collectively called component based frameworks.
4.1.1 Component based Web frameworks
In the Java environment, component based frameworks, also known as a pull-based architectures, duly follow the MVC (Model-View-Controller)1
pattern.
As opposed to the action-based architectures2 these frameworks start
1. MVC is an architectural pattern which separates an application into three main components: the model(business logic), the view (user interface), and the controller (user input) [35].
2. Action-based (push-based sometimes), since they use actions that do required processing and then push the data to the view layer, examples: Spring MVC, Struts [36].
with the view layer by providing set of core components which pull results from multiple controllers3 as needed. [36] This means that, the
components are managing the requests on their own, they do not need the developer to implement various interfaces to achieve the desired functionality. Examples of such frameworks are: JSF (Java Server Faces), Tapestry, Wicket, GWT (Google Web Toolkit), Vaadin as well as Stripes. Component oriented4 frameworks can be identified also in other environments apart from Java. For instance ASP.NET Web Forms is a framework which also provides set of components for which the component model can be created.
The last example of the environment where Web components play a significant role is JavaScript environment, an example is the jQuery UI framework.
All above mentioned frameworks have one important characteristic in common: each component has predefined the HTML mark-up code which is rendered on the client side (browser) and also the JavaScript sources which are either included or imported to the page sources rendered by the browser.
This particular specific of these frameworks enables us to create an implementation of the predefined abstract component model in a way that allows all users of that framework to employ it.
4.1.2 Model requirements
From the motivation of creating such a model as in4.1 arise model requirements, which can be defined as follows:
• The API should be written in the way, that would provide methods for basic use cases of the particular component. In other words, the majority of the components from different frameworks should be able to implement that particular API.
• It should provide cross browser compatibility for testing.
• It should support rapid development and enhance a type safety.
3. Controller is that part of the MVC which handles user input and controls the data flow from business logic to the View layer.
• It should be integrated with selected framework (Arquillian Drone) from tools evaluation, chapter 3.
Those are the general requirements. However, there were other demands on the abstract model. Particularly, we need to define a mech-anism to determine what component we are communicating with, since there can be several components of the same type on one tested web page.
The created API and its implementation should encourage testers to use object oriented testing patterns (see the following section).
4.2
Object Oriented UI Testing Patterns
Regarding to model requirements we are identifying various UI testing patterns. one of them is Page Objects patter. As all patterns, this one is also a result of experience, and this comes particularly from automation of UI tests.
4.2.1 Page Objects
Its main objective is to model Web page areas into objects to reduce the amount of the duplicated code and also to enhance the tests main-tainability [37].
The key principles used to achieve this and best practices for working with Page Objects are [37]:
• the HTML structure of the page is defined only in one place - so that the change in the HTML code will affect tests as little as possible
• Page Objects expose services to developers, in other words provide methods to interact with page
• these methods should again return other Page Objects - this encourages test developers to interact rather with the services than with the implementation. As a result they are able to control which tests will fail. Easily said, it means better maintainability
• Page Objects do not have to represent whole web site, they can be just part of it
An example of such Page Object can be the online translate service (Google Translate5) provided by Google Inc.
The primary service it provides is of course translating from one language to another. But to do so, user often need to choose both from and to which language to translate particular word.
Programmatically speaking, implementing the Page Object for such a Web page using the WebDriver can be written as the code snippet 4.3. The connection between this code and the Google Translate page is represented in a more comprehensive way in figure 4.2.
Figure 4.2: Screenshot of Google Translate page (20th April 2012) with references to the code lines from 4.3.
1 public c l a s s G o o g l e T r a n s l a t e { 2
3 @FindBy ( xpath = " // i n p u t [ @type=’ submit ’ ] ") 4 private WebElement t r a n s l a t e B u t t o n ;
5
6 @FindBy ( xpath=" // t e x t a r e a [ @id=’ s o u r c e ’ ] ") 7 private WebElement i n p u t A r e a ;
8
9 @FindBy ( xpath=" // span [ @id=’ r e s u l t _ b o x ’ ] ") 10 private WebElement r e s u l t A r e a ; 11 12 public S t r i n g t r a n s l a t e ( S t r i n g word ) { 13 i n p u t A r e a . sendKeys ( word ) ; 14 t r a n s l a t e B u t t o n . c l i c k ( ) ; 15
16 (new WebDriverWait ( webDriver , 4 ) ) . u n t i l (new
E x p e c t e d C o n d i t i o n <Boolean >() { 17 18 public B o o l e a n a p p l y ( WebDriver d ) { 19 return ( r e s u l t A r e a . g e t T e x t ( ) . t r i m ( ) . l e n g t h ( ) > 0 ) ; 20 } 21 } ) ; 22 23 return r e s u l t A r e a . g e t T e x t ( ) . t r i m ( ) ; 24 } 25
26 public void s e l e c t L a n g u a g e T o T r a n s l a t e T o ( Language
l a n g u a g e ) {
27 // implemented i n t h e s i m i l a r way 28 }
29
30 public void s e l e c t L a n g u a g e T o T r a n s l a t e F r o m ( Language
l a n g u a g e ) {
31 // implemented i n t h e s i m i l a r way 32 }
33 }
Listing 4.3: Google Translate - example of Page Object written in Java programming language.
As it is in accordance with the best practices this Page Object can be used in the functional test of the translating service as demonstrated by the code snippet.
What was meant by best practices is that only theGoogleTranslate
object distinguishes the HTML structure of the real web page. Better said, it holds this information in WebElement objects - e.g. it knows that the translated result area has id set to result_box value. This Page Object can be used in the functional test of the translate service as demonstrates the code snippet.4.46
Let’s suppose that you want to use interaction with the Google
Translate in other test scenarios, or rather in other test methods, classes or even projects. Then imagine that the Translate button will be removed, and the translation will be triggered automatically, during filling in the input the particular word by releasing the key.
The above mentioned does not have to be just artificial change scenario. This can be easily done later for rendering that page on mobile devices, as the automated translation has already been implemented. Furthermore, it is needed to render as few items on mobile devices as possible.7
Then the only place which will need to be changed will be the Page Object, all dependent test classes will remain untouched. Hereby we reach the goal, enhancing tests maintainability.
1 public c l a s s T e s t G o o g l e T r a n s l a t e extends A r q u i l l i a n { 2 3 @Page 4 private G o o g l e T r a n s l a t e g o o g l e T r a n s l a t e ; 5 6 @Drone
7 WebDriver webD river ; 8 9 @Test 10 public void t e s t T r a n s l a t e W o r d ( ) { 11 g o o g l e T r a n s l a t e . s e l e c t L a n g u a g e T o T r a n s l a t e F r o m (new Language (" s k ") ) ; 12 g o o g l e T r a n s l a t e . s e l e c t L a n g u a g e T o T r a n s l a t e T o (new Language (" en ") ) ; 13 14 S t r i n g w o r d T o T r a n s l a t e = " ryba "; 15 S t r i n g e x p e c t e d T r a n s l a t i o n = " f i s h "; 16 17 S t r i n g a c t u a l T r a n s l a t i o n = g o o g l e T r a n s l a t e . t r a n s l a t e ( wordToTranslate , webDriver ) ; 18
7. The reason is obvious, as mobile devices have smaller screens, mobile application developers need to save space as well as size of requested data.
19 a s s e r t E q u a l s ( a c t u a l T r a n s l a t i o n , e x p e c t e d T r a n s l a t i o n , " The word was t r a n s l a t e d i n c o r r e c t l y ! ") ;
20 } 21 22 }
Listing 4.4: Example of the Arquillian functional test of the Google Translate page, written in the Java language.
In the abstract model created for this thesis, Page Objects are supported as demonstrated on lines 3 and 4 in figure 4.4. Note the differences between default WebDriver project and created model.
Prior to the usage of the particular Page Object, this object needs to be initialised properly. It means that all WebElements of that Page Ob-ject (lines 4, 7, 10 in code snippet 4.3) need to be initialised with a proper object so that they would not encounter NullPointerException.8
As opposed to WebDriver project, this initialisation is done with use of a factory method. We have implemented Page Java annotation(line 3 in code snippet 4.4) which will be recognized in the runtime by Drone extension and initialized with particular Page Object. Further information about integration with this extension is described in section 4.3.2.
4.2.2 Component Objects
Another pattern, or better said good OO (Object Oriented) approach to design reusable code is Component Object. It is very similar to the Page Objects. It also indentifies parts of the Web page and its services, however in a finer grained way. The objective of this is to provide components which are highly reusable. This pattern will be used mainly for creation of the API, its consequences are particular components.
It is based on Component-Based Architectural Style. This style provides a higher level of abstraction by decomposing of the design into logical or individual components that expose well-defined interfaces containing methods, events, and properties.
Components designed in this style should follow following principles [38]:
• Reusable - they should be designed in a way to be easily reused in other parts of the same or different application;
• Replaceable - components should be easily replaced by other components;
• Not context specific - the state should not be included into components, it should be passed to them;
• Extensible - to provide a new behaviour, a component should be easily extended;
• Encapsulated - basic OO rule that methods should not expose the internal implementation, variables or state;
• Independent - in order to use components in other environments, there should be minimal number of the component’s dependen-cies.
Abstract component model implemented for this thesis are trying to meet this requirements. Components are reusable across all projects, they are defined in the Java, but the implementation can describe any component-based Web framework. 4.1.1 They can be easily added to your project as Apache Maven.
One implementation of the component can be replaced by another. This is achieved by defining at first common interface for that component. Then, with regards to the another OO design principle, program to an interface, not an implementation [39], the component dependency can be replaced by the other component, as the second component also implements the common interface.
The interfaces for components do not contain state, however, the implementation does. This is the specific of the component-based frame-works. The framework that the HTML code generated for the particular component is the same for all applications. More information about this specific can be found at the section 4.1.1. Another state which needs to be accessible for components, is which browser they are rendered on. However, this information is injected into them in runtime by Ar-quillian DI (Dependency Injection) mechanism, so it is not wired with components.
The implemented components are also extensible, because the imple-mentation classes are non final and define public constructors, or to be more precise they do not define private constructors. The new behaviour can be then added by the standard extension Java mechanism. It is then on the developer, whether his components will be extendable or not, but the standard ones, from the abstract model are.
Components are encapsulated by encapsulating all the methods, in other words, the Javadoc9 and the names of the methods are not
exposing how their service is done, which means that the particular implementation is hidden.
Let’s take the Google Translate Web page into consideration one more time. This time the whole page will be divided into smaller parts.
Figure 4.3: Google Translate Web page screenshot (20th April 2012) with components identified.
Several components can be identified in the Web page from figure4.2. These components can be used in other pages. Among the simple components we can see following: button, select and text area, continuing with more complex like tab panel or virtual keyboard. The figure 4.3 shows a graphical representation of those.
9. Javadoc - way of creating documentation for Java methods, classes and other structures.
4.3
Implementing the Component Model
All previous sections described our component model in general way. Following sections will describe the implementation in detail.
4.3.1 Employed Java technologies
Above all, we need to define which specifics of Java programming language we used for implementating component model.
Java Reflection
Reflection is a feature of Java language, used by programs to examine, or modify the runtime behaviour of applications. It is an advanced feature which needs to be employed with following concerns kept in developer’s mind [40]:
• Performance overhead, because Java virtual machine10 (JVM) optimizations can not be done for dynamically resolved reflection types, parts of the code which use reflection have slower perfor-mance, and therefore in performance demanding applications, using of reflection should be considered carefully;
• Security restrictions, when the application is running under se-curity manager11, some of the required reflection permissions do
not have to be permitted;
• Exposure of internals, because with use of reflection operations, it is for example possible to access private12 fields and methods, it can have unexpected side-effects, and can destroy portability of the application. Reflection behaviour can be changed with upgrades of Java platform, which can cause that application cease to function.
10. The environment where all Java applications run.
11. A mechanism in Java to determine security policy of the application, that is actions which are permitted to perform in an application.
12. Private is key word in Java to determine fields and methods which can be directly accessed only from the class, where they are defined.
Listing 4.4 shows an example of Java class and its fields declared on lines 4 and 7, its method on line 10. That class does not have defined constructor explicitly, therefore a default one is defined for it.
Wile using reflection it is possible to for example access and manip-ulate this class, its fields and constructors during runtime. Listing 4.5 shows an example of such manipulation with this class. On line 5, all declared fields are retrieved. On line 7 we are iterating over this array of all declared fields, retrieving the name of the field, its annotation Page if exists, information whether the field is accessible, and its generic type. On line 20, there is a demonstration of a way of creating new instances of classes.
1 public c l a s s S h o w c a s e R e f l e c t i o n C a p a b i l i t i e s { 2
3 public s t a t i c void main ( S t r i n g [ ] a r g s ) throws
I n s t a n t i a t i o n E x c e p t i o n , I l l e g a l A c c e s s E x c e p t i o n { 4 5 F i e l d [ ] d e c l a r e d F i e l d s = T e s t G o o g l e T r a n s l a t e .c l a s s. g e t D e c l a r e d F i e l d s ( ) ; 6 7 f o r( F i e l d i : d e c l a r e d F i e l d s ) { 8 S t r i n g name = i . getName ( ) ; 9 Page a n n o t a t i o n = i . g e t A n n o t a t i o n ( Page .c l a s s) ; 10 boolean i s A c c e s s i b l e = i . i s A c c e s s i b l e ( ) ; 11 Type t y p e = i . getType ( ) ; 12 13 System . o u t . p r i n t l n ( name ) ; 14 System . o u t . p r i n t l n ( a n n o t a t i o n ) ; 15 System . o u t . p r i n t l n ( i s A c c e s s i b l e ) ; 16 System . o u t . p r i n t l n ( t y p e ) ; 17 System . o u t . p r i n t l n ( ) ; 18 } 19 20 T e s t G o o g l e T r a n s l a t e i n s t a n c e = T e s t G o o g l e T r a n s l a t e .c l a s s. n e w I n s t a n c e ( ) ; 21 System . o u t . p r i n t l n ( i n s t a n c e ) ; 22 } 23 }
Listing 4.5: Java Reflection example.
List of Java Reflection features is not completed with these few ones described, but these are almost all, which were used in the