The first consideration is how to create integration tests. Depending on how you define them, they could be standard NUnit tests or they could be Cactus or HTTPUnit type tests which call code executing from within the application server. However, Selenium offers a simple alternative to these approaches that can be easily integrated with Maven. Selenium consists of an IDE (a Firefox add-on) that can be used to record and automate actions (such as clicking links and selecting options in a select box) made within the browser. These can be then exported in a number of formats, including as a JUnit test (in fact, a SeleneseTestCase which extends TestCase). I’m aware that this approach to integration testing verges on functional testing. However, I think the boundaries between unit, integration and functional testing are blurry If it makes you feel any better, pretend this blog is called "Automated Functional Testing with Maven, Selenium, Failsafe, Jetty and DBUnit".
Here’s quick overview of how it all works. There are plenty of good tutorials about Selenium and using the IDE to write tests, so I won’t go into it here. After you have recorded your test case in the IDE, you can export it in Java format.public class MyITCase extends SeleneseTestCase { public void setUp() throws Exception { setUp("http://localhost:8080/", "*firefox"); } public void testMy() throws Exception { selenium.open("/myapp/login.htm"); selenium.type("j_username", "percy.project-manager"); selenium.type("j_password", "password"); selenium.click("//button[@type='submit']"); selenium.waitForPageToLoad("30000"); selenium.click("link=Client list"); selenium.waitForPageToLoad("30000"); selenium.click("link=View"); selenium.waitForPageToLoad("30000"); } }In order to play-back the SeleneseTestCase, the Selenium Maven plug-in is needed. This is a “server” which runs an instance of the browser to be remote controlled by the SeleneseTestCase. The plug-in starts the Selenium server in readiness for test cases during the pre-integration-test phase and stops it afterwards (in post-integration-test).
org.codehaus.mojo selenium-maven-plugin 1.0 start-pre-integration pre-integration-test start-server true stop post-integration-test stop-server
In addition to needing the Selenium server, you will need a method to fire off the integration tests during the “integration-test” phase of the Maven build. Obviously, you could use the Surefire plug-in for this. However, the configuration of the plug-in gets a bit more tricky as you need to exclude the unit tests from the integration test and vice verse – especially the integration tests during unit testing as the Selenium server (and Jetty) won’t be running during the unit test phase.
There is a way to do this by using inclusion and exclusion filters within the configuration of the Surefire plugin and I have seen several explanations of how to do this. However, there is a fork of the Surefire plugin called Failsafe which is designed for integration testing. By default, it only runs tests with that contain IT and ITCase in the name. The main benefit is that the Failsafe plugin will continue to run after a test failure or error. That may seem to be a bit strange, but it is intentional. It is to ensure that the post-integration-test phase is executed, which might perform vital clean-up and tear-down operations that should run after the tests are finished. Additionally, it seems to have an enhanced class loader architecture compared to Surefire to handle some more exotic test scenarios – but that’s not relevant to this discussion.
Here is the necessary configuration for the Failsafe plugin.org.codehaus.mojo failsafe-maven-plugin 2.4.3-alpha-1 integration-test integration-test verify verify
Now we have created our integration tests, and have a means of playing them back during the integration-test phase of the Maven build. However, we also need to fire up an application server to run the application itself during the integration-test phase. For this, the Jetty plug-in is ideal. It can be configured to start during the pre-integration-test phase and stop during the post-integration-test phase.
org.mortbay.jetty maven-jetty-plugin 6.1.16 10 8005 STOP start-jetty pre-integration-test run 0 true stop-jetty post-integration-test stop
I recommend using the jetty:run goal rather than jetty:run-war or jetty:run-exploded (although both of these work too). That is because the latter two goals cause the unit tests to run a 2nd time when the application folders are assembled in the target/ directory. However, if you don’t mind sitting through a 2nd run through of your unit tests then this doesn’t matter.
At this point you might be happy to stop, but there is one final step you might wish to take which is to initialise your database prior to the integration-test phase. This can be done with the SQL Maven plug-in (to create the schema) and the DBUnit plug-in (to insert test data). These can both be configured to run during the pre-integration-test phase. The reason for doing this is that if you are using assertions in your Selenium case (i.e. assertTrue( isElementPresent(//div[@id=’content’]/table[@id=’results’]/tbody/tr[3]/td[2])); then you need to be certain that the data in the database is going to be correct with respect to these assertions.DBUnit configuration example.org.codehaus.mojo sql-maven-plugin 1.2 com.microsoft sqljdbc 1.2.0 com.microsoft.sqlserver.jdbc.SQLServerDriver jdbc:sqlserver://mssql2005server:1433;databaseName=MyDatabase;SelectMethod=cursor secret squirrel create-schema pre-integration-test execute true src/main/database/schema-mssql.sql
Hope it helps!org.codehaus.mojo dbunit-maven-plugin com.microsoft sqljdbc 1.2.0 com.microsoft.sqlserver.jdbc.SQLServerDriver jdbc:sqlserver://mssql2005server:1433;databaseName=MyDatabase;SelectMethod=cursor secret squirrel src/test/resources/web-test-data.xml MSSQL_CLEAN_INSERT flat pre-integration-test operation