Archive for the ‘development’ Category

What’s wrong with current web testing approaches

September 13th, 2008

Of late I have been taking a very keen interest in the area of web-testing (I wrote previously on automating testing with selenium ). It is not an easy task to tackle efficiently.

Of course you can get a team of QA’s to write up tests in your favourite web testing tool. How much time/effort/money does this cost? What about when your web application starts to change? What if I change some pages dramatically – how will my tests change?

From my experience the issue isn’t getting tests written up and working. It is more so the maintainability of the tests which determine the success of your web-testing strategy. I have looked at various products and frameworks which are available today in this space and there is a common approach to testing that 99% of them take or suggest to take (via their IDEs and docs).

The approach is quite simple and leverages aspects of the web ‘ecosystem’ -

  • html identifiers via ids and/or xpath expressions
  • wait times for page loads and ajax
  • user actions such as clicks, hover and typing

You write tests using a combination of the above to simulate what a user would do on your web application. To give an example (taken from my previous post) of how this would like look in Selenium -

public class AuthenticationTests extends AbstractSeleniunTest {

    @Test
    public void testUserLogin() throws InterruptedException {
        selenium.open("/");
        selenium.click("link=Login");
        waitForElement("login-form_j_username", "Login not shown");
        selenium.type("login-form_j_username", "xxxx@xxxx.com");
        selenium.type("login-form_j_password", "xxxx");
        selenium.click("login-form_0");
        waitForElement("user-home-link", "User home link not shown");
        assertTrue(selenium.isElementPresent("user-home-link"));
    }
}

Line 5: direct references are made to pages to open
Line 6: things to click by name
Line 7: wait for something
Line 12: check if some field is present identified by its id

There is, I think, a very big problem with writing tests in this way. The test cases are very tightly coupled to the html code of the web application. If this code changes – your tests will start to break. This may or may not be a problem for most people however if you are working on a dynamic web application – this is most likely a very big problem. As I alluded to earlier, this problem is not specific to Selenium which I have used to illustrate the example.

Page Object Pattern

A design pattern which is emerging that addresses this issue is the Page Object pattern. I first read about it here and have seen mentions of it else where. As with all design patterns, people are almost certainly already using it (that is where patterns come from) and now has been given a name. Also something to note is the items raised in this post and the pattern itself are not confined to web applications – it covers any GUI based testing.

The ‘Page Object’ pattern is akin to the Facade design pattern from the GoF. The PageObject is a layer of abstraction called which hides away the details of the page implementation. This object then exposes only actions/methods relevant to the tests (e.g. user actions, specific test methods).

The pattern very much follows the principle of seperation of concerns. The tests are not concerned about the ‘id’ of a particular ‘div’ or the name of a button, nor should they be. Instead they should only be concerned with actions a user may do on a page and additional operations required for the test cases (there are exceptions). In this way, the test cases should begin to develop an immunity to changes in the GUI.

Below is an example of the previous test rewritten using the Page Object pattern. I have left out the actual PageObject’s to keep this post to a sane length. Inside the PageObject, the actions and methods are driven by the same web constructs discussed in the beginning of the post (ids, clicks etc). In later posts I will show how to implement the actual PageObject’s and test cases using Selenium.

public class AuthenticationTests extends AbstractSeleniunTest {

    @Test
    public void testUserLogin() {
        IndexPage index = new IndexPage(); // Open home page
        LoginPage loginPage = index.loginPage(); // Go to login page
        index = loginPage.login("user", "password"); // Login entering user and password
        assertTrue(index.isLoggedIn()); // Ensure I am logged in
    }
}

Line 5: IndexPage PageObject opened (i.e. open “/”)
Line 6: Go to login page (LoginPage PageObject returned)
Line 7: Login by passing username and password to the login method
Line 8: check if I am logged in

Looking at the above, it is quite clear that even if the html of my page changes the test case will not break (not a single html related entity is referenced). Test cases won’t break in this case – the PageObject(s) will. This is a very important distinction because the majority of the effort and work will be in the test cases. The PageObject’s will be reused by many tests and should be relatively small classes with simple methods.

Another very important characteristic of the above test case is the language/constructs used in the above test case are ‘natural’ and intuitive. This allows the non-technical to define test cases in a manner very similar to the actual tests themselves. Given the right tools, it would be quite easy for testers and analysts to write test cases using this pattern which are maintainable and more resistant to lower level changes.

As mentioned earlier, I will follow up this post in the next week or so with an actual test case and implementation using Selenium.

Hopefully someone in the field is reading..

Hibernate Query Performance Logging

June 22nd, 2008

One critical feature missing in Hibernate is the ability to log how long queries are taking. This is vital when you are performance tuning your application to see which parts can be optimized and also can highlight issues with the database (missing indexes, fragmentation etc.). With the correct fetching and batch strategies you usually don’t have to do too much, but it is always good to know how long things are taking. It can help in defining the order in which queries should run and even identify that maybe you should be doing queries in parallel.

There are configuration options already which allow you to print the sql queries being run but nothing I have seen which allows me to do the above. btw. if anybody knows how to print the full sql query with the parameters I would love to know.

One can always write logging statements surrounding each method (usually DAO methods) running the queries, however this is quite pervasive. It would be nicer to do and change as little as possible and get query execution times. Aspect-oriented programming can help us here. The following piece is an example using Spring+Hibernate stack since this is what I use – and it makes it so easy!

For a full description on AOP head-over to wikipedia linked above – I will give a very short crash-course. The idea behind AOP is the separation of concerns. We have packages, classes and methods which assist us with separation and modularization. Some concerns though don’t fit into this idea – they ‘crosscut’ other concerns. An example is logging. Aspect oriented programming gives us additional tools by which we can separate these sorts of ‘concerns’.

So firstly the code -

package au.com.xyz.aop;

import org.apache.commons.lang.time.StopWatch;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class DAOInterceptor {
    protected Log log = LogFactory.getLog(DAOInterceptor.class);

    @Around("execution(* au.com.xyz.dao..*.*(..))")
    public Object logHibernateQueryTimes(ProceedingJoinPoint pjp) throws Throwable {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        Object retVal = pjp.proceed();
        stopWatch.stop();

        String str = pjp.getTarget().toString();
        log.debug(str.substring(str.lastIndexOf(".")+1, str.lastIndexOf("@")) + " - " + pjp.getSignature().getName() + ": " + stopWatch.getTime() + "ms");
        return retVal;
    }
}

The AOP class is a simple POJO with some annotations -

@Component is Spring related – so Spring will create an instance of the bean when it scans.
@Aspect declares that this class is an Aspect
@Around is Aspect related – means that execute this method around the methods in the classes given by expression (i.e. before the actual method is called and after the method is called)
In the actual method you can see it is again very simple -

Lines 19-20: I start a timer (commons StopWatch)
Line 21: Invoke the DAO method which will run the query and keep the return value
Line 22: Stop the timer
Line 24-25: Log the time taken for the query
Line 26: Return the value of the DAO method
The only other thing left to do is to configure Spring to enable AOP.



    


That’s it! Depending on how you’ve configured your logging, you should see output similar to the following in your log -

17:51:48,411 DEBUG au.com.xyz.aop.DAOInterceptor - CountryDAO - findAllCountries - 20ms
17:51:48,435 DEBUG au.com.xyz.aop.DAOInterceptor - CountryDAO - findCountriesStartingWith - 34ms

Update!

A user commented on this useful log4j configuration which I thought I would post.

### Hibernate logging configuration ###

### Log everything (a lot of information, but very useful for troubleshooting) ###
#log4j.logger.org.hibernate=info

### Log HQL and SQL ASTs during query parsing ###
log4j.logger.org.hibernate.hql.ast.AST=DEBUG, SQL_APPENDER
log4j.additivity.org.hibernate.hql.ast.AST=false

### log just the SQL
log4j.logger.org.hibernate.SQL=DEBUG, SQL_APPENDER
log4j.additivity.org.hibernate.SQL=false

### log JDBC bind parameters. Very userfull, when debug parameterized queries ###
log4j.logger.org.hibernate.type=TRACE, SQL_APPENDER
log4j.additivity.org.hibernate.type=false

### log schema export/update ###
#log4j.logger.org.hibernate.tool.hbm2ddl=info

### log HQL parse trees
#log4j.logger.org.hibernate.hql=debug

### log cache activity ###
#log4j.logger.org.hibernate.cache=info

### log transaction activity
#log4j.logger.org.hibernate.transaction=debug

### Log all JDBC resource acquisition
#log4j.logger.org.hibernate.jdbc=debug

### enable the following line if you want to track down connection ###
### leakages when using DriverManagerConnectionProvider ###
#log4j.logger.org.hibernate.connection.DriverManagerConnectionProvider=trace

log4j.appender.SQL_APPENDER=org.apache.log4j.RollingFileAppender
log4j.appender.SQL_APPENDER.File=${applicationRoot}/log/sql.log
log4j.appender.SQL_APPENDER.MaxFileSize=1000KB
log4j.appender.SQL_APPENDER.MaxBackupIndex=62
log4j.appender.SQL_APPENDER.layout=org.apache.log4j.PatternLayout
log4j.appender.SQL_APPENDER.layout.ConversionPattern=[%d] %5p [%t] (%F:%L) - %m%n

Hibernate Dynamic Table Routing

June 13th, 2008

I have been searching for a method to dynamically route objects to databases at runtime using Hibernate and recently I found a solution which fit the bill. This post explores the problem and identifies one possible solution.

The Problem:

I have a web-application (using Spring+Hibernate for ORM) which makes use of two database schemas (in the same physical database). I specify a datasource accessible via JNDI (using tomcat achieved via context xml) so across my different deployment environments I can use the same war file. This is particularly important as I do not want to use different binaries for different environments. My ethos is to keep any dynamic information pertinent to the deployment environment strictly outside of the war file exactly for the above reason – same war file across deployments.

I specify one DS (datasource) since the spring hibernate session-factory expects a single DS. But this only gets my half way there – I have only specified one database connection string – what about the other database? You can have multiple session factories but then have to deal with cross session transactions.

We can solve this problem (not the main problem this post addresses) by using the ’schema’ and ‘catalog’ configuration items on our entities which are in the DB #2. Below is an example which I will use for reference involving 3 objects spanning 2 schemas. It involves a common practice where identity management information is kept in a separate database to the application(s) database. I have omitted most of the information from the objects for brevity.

@Table
@Entity
public class ApplicationUser {
    private IdentityUser identityUser;
}

@Table (schema="secure", catalog="identitymanagement")
@Entity
public class IdentityUser {

    @OneToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "IdentityUserIdentityRole", schema="security", catalog="identitymanagement",
    joinColumns = {@JoinColumn(name = "IdentityUserID")}, inverseJoinColumns = {@JoinColumn(name = "IdentityRoleID")})
    private Set<IdentityRole> roles;
}

@Table (schema="secure", catalog="identitymanagement")
@Entity
public class IdentityRole {
}

ApplicationUser is in DB #1 and is configured via my JNDI datasouce. Its schema and catalog can be configured via default hibernate properties. As can be seen, I have hard-coded the schema and catalog values for the identity management objects IdentityUser and IdentityUserRole. I cannot put dynamic elements in these annotations and also note that using hbm.xml files does not get around this (while also keeping a single binary across deployment environments). This is the core of the problem.

What this means is that I can have multiple instances of DB #1 and only a single instance of DB #2 (which must be named secure.identitymanagement.*) in any single deployment environment. Not very tenable.

The Solution:

The way hibernate builds its queries is that it prepends the schema and catalog names when referring to the tables – i.e. in my example we would end up with a queries to the effect of – “select * from secure.identitymanagement.IdentityUser”. What we need is a way of manipulating this query building.

Enter the hibernate interceptor.

public class HibernateInterceptor extends EmptyInterceptor {

    @Override
    public String onPrepareStatement(String sql) {
        String prepedStatement = super.onPrepareStatement(sql);
        prepedStatement = prepedStatement.replaceAll("secure.identitymanagement", "my_dynamic_goodness");
        return prepedStatement;
    }

}

This interface is probably one of the most powerful hibernate features in the framework. It allows you to get at the core of Hibernate and manipulate properties at execution time – in this instance to do dynamic table routing. The ‘onPrepareStatement’ is obviously called when Hibernate is preparing the sql statements just before it sends it to the DB. What the code is doing (if it isn’t obvious) is a simple string replacement of an exact string. This exact string is what we hard-code (can be anything but should be an identifier which will not occur otherwise in sql statements) and is to be replaced by the dynamic catalog and schema name. Where the actual catalog and schema name values come from is up-to the implementer. In the case of a web-application, I code this in the context as a String property and look it up via JNDI similar to the DS.

I have deliberately made the above code very simple to show the underlying concept. One could put some quite complicated routing logic in there to do other forms of dynamic routing (e.g. based on logged in customer, based on users region). There are other solutions to the dynamic routing problem which address slightly different use-cases – check Hibernate Shards and dynamic models.

I am not sure the interceptor was meant for what I am ‘hijacking’ it to do but the first line of the api does give some insight…
Allows user code to inspect and/or change property values.

Automate Website Testing with Selenium RC

May 18th, 2008

One of the difficulties I face when we do a release is regression testing – specifically testing the front-end (website) to ensure everything is working/looking correct. At the moment we have a team of 2 testers who work almost feverishly when a release is getting close to ensure that it is indeed a good release. To address this issue I have been looking at Selenium and how we can use it to automate a large portion of this regression testing.

I come from a Java background where we use Continuous Integration so I wanted to integrate Selenium testing as part of our build cycle. Selenium comes in a number of forms – IDE, Core, RC and Grid (some of which work together). In this article I will be focusing on Selenium RC – to find what the others are about head over to the Selenium website.

Since the website I work on utilizes AJAX in quite a number of places, one of the requirements I had for Selenium was that it had to be able to deal with AJAX enabled sites. Although the Selenium website doesn’t specifically talk about it, I did find help regarding the subject in their user-forums.

Below is a short write-up on the steps I took to get some basic tests running via Selenium RC. Note that you should download the Selenium RC package to get the java client libs and the Selenium server – available here.

1. Selenium Test Case

First I setup a JUnit 4 abstract class to setup selenium before my actual test class runs. You can see below in my AbstractSeleniumTest class in the setUp method I construct a DefaultSelenium object by passing in parameter values obtained from the System – this is so that I can test different environments (local dev, integration, uat etc.) configured at runtime. Also to note are the use of the new JUnit4 annotations.

public abstract class AbstractSeleniumTest {

    protected static Selenium selenium;
    protected static String SELENIUM_SERVER_HOST="selenium.server.host";
    protected static String SELENIUM_SERVER_PORT="selenium.server.port";
    protected static String SELENIUM_BROWSER_STARTCOMMAND="selenium.browser.startCommand";
    protected static String SELENIUM_BROWSER_URL="selenium.browser.url";

    @BeforeClass
    public static void setUp() {
        selenium = new DefaultSelenium(System.getProperty(SELENIUM_SERVER_HOST),
        Integer.parseInt(System.getProperty(SELENIUM_SERVER_PORT)),
        System.getProperty(SELENIUM_BROWSER_STARTCOMMAND),
        System.getProperty(SELENIUM_BROWSER_URL));
        selenium.start();
    }

    @AfterClass
    public static void tearDown() {
        selenium.stop();
    }

    public void waitForElement(final String waitingElement, String timeoutMessage) {
        new Wait() {
            public boolean until() {
                return selenium.isElementPresent(waitingElement);
            }
        }.wait(timeoutMessage);
    }
}

Now my class which will test an AJAX’ified login box.

public class AuthenticationTests extends AbstractSeleniunTest {

    @Test
    public void testUserLogin() throws InterruptedException {
        selenium.open("/");
        selenium.click("link=Login");
        waitForElement("login-form_j_username", "Login not shown");
        selenium.type("login-form_j_username", "xxxx@xxxx.com");
        selenium.type("login-form_j_password", "xxxx");
        selenium.click("login-form_0");
        waitForElement("user-home-link", "User home link not shown");
        assertTrue(selenium.isElementPresent("user-home-link"));
    }
}

2. Setting up Selenium Server

Selenium server needs to be setup on the box on which you plan to run the tests from and this box should also have some web browsers installed. I use a mac so I am testing under firefox (note – that firefox3 beta does not seem to work yet so ensure you have firefox2 ).

Once you have extracted the Selenium RC download, run the server as below.

computer:selenium-server-1.0-beta-1 alvins$ pwd
~/selenium-remote-control-1.0-beta-1/selenium-server-1.0-beta-1
computer:selenium-server-1.0-beta-1 alvins$ java -jar selenium-server.jar -interactive
alvin-singhs-computer:selenium-server-1.0-beta-1 alvins$ java -jar selenium-server.jar -interactive
23:18:15.624 INFO - Java: Apple Inc. 1.5.0_13-119
23:18:15.625 INFO - OS: Mac OS X 10.5.2 i386
23:18:15.628 INFO - v1.0-beta-1 [2201], with Core v1.0-beta-1 [1994]
23:18:15.724 INFO - Version Jetty/5.1.x
23:18:15.725 INFO - Started HttpContext[/,/]
23:18:15.726 INFO - Started HttpContext[/selenium-server,/selenium-server]
23:18:15.727 INFO - Started HttpContext[/selenium-server/driver,/selenium-server/driver]
23:18:15.732 INFO - Started SocketListener on 0.0.0.0:4444
23:18:15.732 INFO - Started org.mortbay.jetty.Server@2a5330
Entering interactive mode… type Selenium commands here (e.g: cmd=open&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;1=http://www.yahoo.com)

Note that if you are in a linux headless environment you must have X installed and run Xvfb before running the above.

computer:selenium-server-1.0-beta-1 alvins$ Xvfb :1
computer:selenium-server-1.0-beta-1 alvins$ export DISPLAY=:1

3. Running the Test

I use eclipse for development and so to run the test, right click on the Test class–>Run As–>Open Run Dialog–>Arguments Tab as below. The arguements are quite self explanatory – they are fed into the DefaultSelenium class as can be seen in the code from Step 1. The third argument deserves a mention – this is the browser you wish to run your tests on – some supported browsers include iexplore, konqueror, firefox, safari and opera.

Once you are all set, simply click Run and what should happen is that the test will contact the selenium server, start firefox and start executing the tests. Of course if you are running in a headless environment you will see firefox launch but running in interactive mode you should still be able to see the selenium server logging its activity.

23:24:14.037 INFO - Checking Resource aliases
23:24:14.042 INFO - Command request: getNewBrowserSession[*chrome, http://xxxx.com] on session null
23:24:14.042 INFO - creating new remote session
23:24:14.118 INFO - Allocated session 15a43743285c4dba913a3ff0ab2b5dea for http://xxxx.com, launching…
23:24:14.160 INFO - Preparing Firefox profile…
23:24:18.520 INFO - Launching Firefox…
23:24:20.819 INFO - Got result: OK,15a43743285c4dba913a3ff0ab2b5dea on session 15a43743285c4dba913a3ff0ab2b5dea
23:24:20.830 INFO - Command request: open[/, ] on session 15a43743285c4dba913a3ff0ab2b5dea
…

Depending on your build environment you will have to decide how you want to integrate these tests as part of your build. The Selenium Wiki has some great examples on different ways to do this. My company uses Bamboo (CI Server) from the great people at Atlassian and this runs the tests after successful builds.

Happy testing!