Types of testing

Testify supports two types of testing (well, maybe more :-) ):

Unit testing
where components and pages are tested in isolation from the rest of the application. There is some general infrastructure in the Tapestry IOC but all the key dependencies are passed in from the test as mocks, fakes or stubs of the real services.
Integration testing
where you setup an almost-complete version of the application just substituting any services that represent external dependencies (such as databases or other systems). You can use also use this approach for acceptance testing with tools such as Fitnesse.

If you have not done too much testing before then start with unit testing only and test components only. It's much easier. Integration/acceptance testing has some important benefits but is typically harder to do; so learn your craft on the simpler stuff first.

Unit testing

With Testify you can do test-driven development (TDD) to create Tapestry components and, to some extent, pages. You need to use some particular techniques to test components.

The major Testify feature you need is the ability to Inject objects from the test into components.

The Tapestry IOC container is very clever at instantiating only the services you need. However, I would recommend breaking your application into a variety of Modules and building an IOC for the unit tests that includes only the minimal services you need. For example, do include modules with global customizations of Tapestry (for example, contributions to core services such as TypeCoercer or ValueEncoder) and exclude modules that have services that depend on external systems such as databases. If you do this it will stop your unit tests accidentally connecting to databases because you forgot to provide a mock for that service in your unit test.

Integration testing

The unit testing approach can't always be used for pages. A page typically integrates a lot of components - each needing particular services to be injected - and as soon as it includes something like a site navigation that depends on other pages, managing the injections required becomes impossible at a unit level.

I recommend that your pages are little more than aggregations of components and have very little logic in them because then you can unit test the components. This might mean that you introduce slightly more components than you usually would.

To test pages that are complex aggregations of components with dependencies on other pages, you need to build an almost-complete IOC registry. The key here is to break your application into modules like this:

The core module
contains general tapestry customizations and generic services (use this module in unit tests)
The domain module
contains application-specific services that don't (directly) rely on external systems. Access to external systems is via other services, which are defined in the external module.
The external module
contains services that connect to external systems (such as databases)
The fake external module
contains the same services as the external module except implemented entirely with fake services using the Per-test scope scope.

And here are the configurations that you would use in your tests:

UnitCore
IntegrationCore + Domain + Fake External
Real systemCore + Domain + External