Skip to end of metadata
Go to start of metadata

Testing Tynamo application

Generally speaking, you can test your code at various different levels of granularity. The higher level the test is, the more ground the test typically covers (i.e. executes more lines of code), but the reasons for test failures often become more obscure. Lower-level unit tests are good for pinpointing exact problems but you need a lot of them to build comprehensive coverage over your whole code base. You can often divide your tests into a few different categories, such as unit tests, integration tests and functional tests. Maven has built-in support for executing both unit and integration tests and the Tynamo archetype takes advantage of it. In the archetype, the container-based test is thought to be an integration test and everything else a unit test. Tynamo uses TestNG because Tapestry uses TestNG but you can just as easily use the better known Java unit testing framework, JUnit. TestNG has a few handy things especially for integration testing, but JUnit 4 is roughly on par with TestNG feature-wise.

Unit testing your application

If you need to test helper operations of a page (class), you can simply instantiate the page and inject mocked-up dependencies as needed (use either Mockito, JMock or EasyMock), but Tapestry also provides a built-in PageTester class for unit testing your pages (see Tapestry's Unit testing pages or components guide). With PageTester, you can easily render the page and "click" on its links.

Services can be instantiated manually with mocked-up dependencies just as easily, but often times your services are dealing with persistence layer and you want to specifically test that your Hibernate criterias are working as expected. In that case, there's no substitute for testing against a real database. Fortunately, this is very simple with modern in-memory, embedded Java databases such as H2. Here's one example of a base class that you could use for testing your persistence services:

As you see, the base class will automatically try to roll back the active transaction at the end of the test. You can certainly open multiple transactions and commit them just as long as you properly clean up the modifications after the test execution so your changes won't collide with other tests.

Integration testing

tapestry-model-test contains a base class called AbstractContainerTest that is meant for container-based integration testing. The AbstractContainerTest starts up an embedded Jetty instance, runs your web application in place (i.e. picks up the templates from src/main/webapp) and uses the compiled classes in target/classes rather than building a war file) and initializes a HtmlUnit client for testing your page flow as if it was a real user. As a sample, Tynamo-archetype provides a working integration test that uses AbstractContainerTest.

Tapestry also provides support for Selenium-based tests with tapestry-test module. Selenium uses real browser engines (Firefox, IE) so in theory it can better assert that your web site works for browsers your users use and Selenium tests can be recorded via browser plugin-ins. However, Selenium test automation is more complex to set up, they break more easily, they are harder to fix and they run longer than HtmlUnit/AbstractContainerTest-based tests since the latter is an all-java solution that runs everything in-process. If you are using standard building blocks rather than doing much of custom Javascript, typically HtmlUnit's embedded browser is good enough for executing the whole page logic, including Javascript. In other words, if you break your page flow, you typically break it for all browsers (that at least try to follow some modern html standards), not just one, simulated or not. But this depends on your situation - it's completely up to you what to use for your integration testing and you could certainly use a combination of both HtmlUnit and Selenium-based tests as well. Just remember that you want to go for "the biggest bang for buck" - in practice it's impossible to test everything, so you want to have the simplest, fastest way of testing as many of the most likely scenarios as possible.

  • No labels