11.7 PetClinic Example

The PetClinic application, available onGitHub, illustrates several features of the_Spring TestContext Framework_in a JUnit 4 environment. Most test functionality is included in theAbstractClinicTests, for which a partial listing is shown below:

import static org.junit.Assert.assertEquals;
// import ...

@ContextConfiguration
public abstract class AbstractClinicTests extends AbstractTransactionalJUnit4SpringContextTests {

    @Autowired
    protected Clinic clinic;

    @Test
    public void getVets() {
        Collection<Vet> vets = this.clinic.getVets();
        assertEquals("JDBC query must show the same number of vets",
            super.countRowsInTable("VETS"), vets.size());
        Vet v1 = EntityUtils.getById(vets, Vet.class, 2);
        assertEquals("Leary", v1.getLastName());
        assertEquals(1, v1.getNrOfSpecialties());
        assertEquals("radiology", (v1.getSpecialties().get(0)).getName());
        // ...
    }

    // ...
}

Notes:

  • This test case extends theAbstractTransactionalJUnit4SpringContextTestsclass, from which it inherits configuration for Dependency Injection (through theDependencyInjectionTestExecutionListener) and transactional behavior (through theTransactionalTestExecutionListener).

  • Theclinicinstance variable — the application object being tested — is set by Dependency Injection through@Autowiredsemantics.

  • ThegetVets()method illustrates how you can use the inheritedcountRowsInTable()method to easily verify the number of rows in a given table, thus verifying correct behavior of the application code being tested. This allows for stronger tests and lessens dependency on the exact test data. For example, you can add additional rows in the database without breaking tests.

  • Like many integration tests that use a database, most of the tests inAbstractClinicTestsdepend on a minimum amount of data already in the database before the test cases run. Alternatively, you might choose to populate the database within the test fixture set up of your test cases — again, within the same transaction as the tests.

The PetClinic application supports three data access technologies: JDBC, Hibernate, and JPA. By declaring@ContextConfigurationwithout any specific resource locations, theAbstractClinicTestsclass will have its application context loaded from the default location,AbstractClinicTests-context.xml, which declares a commonDataSource. Subclasses specify additional context locations that must declare aPlatformTransactionManagerand a concrete implementation ofClinic.

For example, the Hibernate implementation of the PetClinic tests contains the following implementation. For this example,HibernateClinicTestsdoes not contain a single line of code: we only need to declare@ContextConfiguration, and the tests are inherited fromAbstractClinicTests. Because@ContextConfigurationis declared without any specific resource locations, the_Spring TestContext Framework_loads an application context from all the beans defined inAbstractClinicTests-context.xml(i.e., the inherited locations) andHibernateClinicTests-context.xml, withHibernateClinicTests-context.xmlpossibly overriding beans defined inAbstractClinicTests-context.xml.

@ContextConfiguration
public class HibernateClinicTests extends AbstractClinicTests { }

In a large-scale application, the Spring configuration is often split across multiple files. Consequently, configuration locations are typically specified in a common base class for all application-specific integration tests. Such a base class may also add useful instance variables — populated by Dependency Injection, naturally — such as aSessionFactoryin the case of an application using Hibernate.

As far as possible, you should have exactly the same Spring configuration files in your integration tests as in the deployed environment. One likely point of difference concerns database connection pooling and transaction infrastructure. If you are deploying to a full-blown application server, you will probably use its connection pool (available through JNDI) and JTA implementation. Thus in production you will use aJndiObjectFactoryBeanor<jee:jndi-lookup>for theDataSourceandJtaTransactionManager. JNDI and JTA will not be available in out-of-container integration tests, so you should use a combination like the Commons DBCPBasicDataSourceandDataSourceTransactionManagerorHibernateTransactionManagerfor them. You can factor out this variant behavior into a single XML file, having the choice between application server and a 'local' configuration separated from all other configuration, which will not vary between the test and production environments. In addition, it is advisable to use properties files for connection settings. See the PetClinic application for an example.

results matching ""

    No results matching ""