Welcome to EMC Consulting Blogs Sign in | Join | Help

Richard Griffin's Blog

WATiN/R Testing Design Pattern

Do you want your tests fixtures to look like this ?

 

        [TestMethod]
        public void EnterInvalidUsernameAndPassword()
        {
            //Navigate to home page
           
IE instance = HomePageManager.NavigatetoHomePage();           

            //Check that there are no items in the basket
           
if (!BasketManager.ValidateBasketIsEmpty(instance))
            {
                //if not clear it
               
BasketManager.ClearBasketItems(instance);
           

            //Sign out of the session
           
SignInManager.SignOut(instance);          

            //Attempt a signin with invalid user name
           
SignInManager.SignIn(instance, "test&tester.com", "fake", true);           

            //make sure that the oops message is visible and the text is correct
           
Assert.IsTrue(instance.ContainsText("Oops! You made a mistake!"));

            Assert.IsTrue(instance.ContainsText("Please enter a valid email address."));
        }

 

In this post I will provide a outline for an approach you can use to refactor your tests and create a more maintainable and flexible design to use in your WATiR/N tests.

 

The WATiR WebRecorder++ and the WATiN WebRecorder++ are great tools that help create test scripts quickly. In the short term they provide the ability to create lightweight test scripts by both the developer and the tester.  The recorders output code so that the user becomes familiar with seeing the language and becomes used to certain constructs in that language. Once the test script has been recorded, verification code is written in the form of asserts to ensure that the required level of functionality is being delivered. Based on these factors, the creation of test fixtures using WATiR/N is a two-stage process consisting of recording and verifying activities.


With a complex or enterprise web site, the test fixture starts to get rather large, rather quickly, bringing with it management and maintainability issues. When creating tests using this particular approach there are some problems; a large amount of copying and pasting happens as test fixtures start to overlap; resulting in duplicate code for testing components that are reused across the site on different pages; and the navigational code for requesting pages in the site is consistently the same. One way around this is to provide a template that contains the default actions that your test fixture requires, record the test and crank out the verification code, copy this and add the code to the template and stash this away. A simple and effective way to improve productivity as the main parts of the test are predefined and heavily tested so if you test script barfs due to coding errors they are isolated to the new code that has been added to the template, allowing you to isolate the issue and quickly fix the problem.


The template helps but does not really solve the problems mentioned above. As a programmer we understand that low coupling and high levels of cohesion helps to make the code base easier to manage and maintain. Therefore, why not apply these principles to the test suite.  Therefore, I created a design that would increase cohesion and remove and need to copy and paste code, Don’t Repeat Yourself (DRY). To implement such a design, there needs to be a separation of concerns, by breaking away data, processing logic and test fixture. At this point it is important note that changes to HTML element id’s can cause your test fixture to break, and there is a question of responsibility over the tests. One of the reason that prompted the refactoring of the tests was based on observations that the developers were not owning the tests. So the tester spent more time maintaining existing tests rather than creating new tests scripts. For the moment it is best that we assume all parties are responsible.


Earlier I identified the three areas to be refactored, data, processing logic and the tests scripts that we want to rinse. Here I am going to classify the id’s and names of HTML elements  as the data part of the puzzle. Each page on the site needs to be mirrored to create a test data object, the data objects are simple property bags containing the id and names of the HTML elements located on the page. By creating this design, it tackles the issues around cohesion mentioned earlier. Having all the names and id’s of elements on the page in one place means that, when there is change on the page updating the variables happens in one place. The result, of the refactoring means that for each page on the web site there is a corresponding property bag class that contains the id’s of HTML elements and a property to access that HTML element.


The simple base structure, contains an abstract class  Abstract Page that implements an interface IPage. The interface provides the ability to access either a secure or an unsecure web page.  All other data classes that are created are derived from the AbstractPage class and therefore all data classes that are created retain the ability to have a secure and unsecure url’s. To adding a new interface or extending the existing IPage interface you will be able to add the extra functionality you require. The design is about containment and isolation, to allow for the auto generation of implemented pages and page components. The secure and unsecure Url properties are populated from a configuration file so that you can point the test scripts at a particular server with ease. A future enhancement that I would like to introduce is the ability for data classes to be auto generated using a CodeSmith template.


So now that we have meet the data aspect of the design, we can move on to the reusable logical processing actions of the  design. The functionality that is exposed or expected to be exposed is presented via a manager of the component or page. Therefore, in the current design there is a manager that you speak with to perform an action. The manager uses the data objects to access the HTML elements on the page to perform the task at hand requested. The processing logic is contained within the manager objects, it is here where you define common tasks on a page or component. Once there is duplication or common functionality requirements then this is the place where the code should live.


 

There is a one to one mapping between the managers and the page or control. Therefore there is a 1 to 1 mapping between the manager and the data class, you will notice that each implementation of the manager class are sealed so that they can’t be extended, they are closed. The next noticeable facet is that each of the managers are thread safe singletons. The managers are created from common code or duplicate code found in the outputted code of the recorder. Such action tasks as, “Login a user with the correct username and password” or a simple action, “navigate to homepage” make up the logical processing. These actions are common and are very reusable, achieving the goal of cohesion and enforcing the separation of concerns that we want from the pattern. They can also be made into your own toolset of controls that after a time provide a comprehensive framework. For instance most web sites that provide a mechanism to order products or service require a login and will have a shopping basket to order and pay for goods. By adding reusable basket testing components to your framework will help you become more productive.


        [TestMethod]
        public void EnterInvalidUsernameAndPassword()
        {
            //Navigate to home page
            IE instance = HomePageManager.NavigatetoHomePage();           

            //Check that there are no items in the basket
           
if (!BasketManager.ValidateBasketIsEmpty(instance))
            {
                //if not clear it
               
BasketManager.ClearBasketItems(instance);
           

            //Sign out of the session
           
SignInManager.SignOut(instance);           

            //Attempt a signin with invalid user name
           
SignInManager.SignIn(instance, "test&tester.com", "fake", true);           

            //make sure that the oops message is visible and the text is correct
           
Assert.IsTrue(instance.ContainsText("Oops! You made a mistake!"));
            Assert.IsTrue(instance.ContainsText("Please enter a valid email address."));
        }


The final piece of the puzzle are the test fixtures When using the recorder mechanism it can be easy to hit a curve where the QA person is spending more time doing maintenance tasks rather than creating test fixtures for the site. These test scripts that are produced can be confusing and prone to error caused by copy and pasting code from other areas of the test suite. To provide a solution to this the QA can now write there tests in a more English notation that can be easily read and understood, by a BA or developer. How is this achieved? This is done through the using interactions with the managers of the page and or the managers of component areas of the site to create a test script for a particular area of the system. Allowing the QA / tester  / developer to concentrate more on the creation of the test rather than the maintaining of the test. By designing layers of abstraction test fixtures can build high level readable tests.

 

The article has covered a mechanism for creating an abstract test design pattern based on using recorders that are commonly available to the development and testing teams. The ability of implementing an abstract design delivers a large amount of reusable code base that can be used on different projects that you work on, and could even be used as benchmarks for new projects starting up that have a similar level of functionality.

Published Tuesday, November 14, 2006 11:41 PM by Anonymous
New Comments to this post are disabled
Powered by Community Server (Personal Edition), by Telligent Systems