4. REST Services Test

The REST Services tests and their test base are for testing the REST clients and services configuration, as well as their calls into the service layer. There is an in memory database and controllers are loaded in an embedded jetty server.

Spring Configuration

There are actually two main Spring contexts. One is the standard test context that just loads the REST clients and a properties file to configure the path to the REST APIs and any other client configuration information.

The other context is loaded in the EmbeddedJetty bean. It takes the list of Spring configuration files and loads them in a parent context, then a servlet child context is created. It also registers the Spring Security filter with the servlet handler.

rest-controller-test-context.xml
                    
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p" 
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/util
                           http://www.springframework.org/schema/util/spring-util.xsd">

    <import resource="classpath*:/META-INF/spring/client/**/*-context.xml"/>

    <util:properties id="restProperties"
                     location="org/springbyexample/contact/web/service/ws.properties" />
                     
    <bean class="org.springbyexample.web.service.EmbeddedJetty">
        <constructor-arg>
            <list>
                <value>/embedded-jetty-context.xml</value>

                <value>/META-INF/spring/security/**/*-context.xml</value>
                <value>/META-INF/spring/marshaller/**/*-context.xml</value>
                <value>/META-INF/spring/db/**/*-context.xml</value>
                <value>/META-INF/spring/services/**/*-context.xml</value>
                <value>/META-INF/spring/mvc/**/*-context.xml</value>
                
                <value>/mock-web-security-context.xml</value>
            </list>
        </constructor-arg>
    </bean>
    
</beans>
                    
                

Abstract Code

The AbstractRestControllerTest sets up the shared Spring test configuration, and before each test resets the DB by re-initializing the schema and clearing the JPA entity manager cache.

Example 7. AbstractRestControllerTest

                    
@ContextConfiguration({ "classpath:/org/springbyexample/contact/web/service/rest-controller-test-context.xml" })
public abstract class AbstractRestControllerTest extends AbstractProfileTest {

    final Logger logger = LoggerFactory.getLogger(AbstractRestControllerTest.class);
    
    @Autowired 
    private EmbeddedJetty embeddedJetty;
    
    /**
     * Reset the DB before each test.
     */
    protected void doInit() {
        reset();
    }

    /**
     * Reset the database and entity manager cache.
     */
    protected void reset() {
        resetSchema();
        resetCache();
        
        logger.info("DB schema and entity manager cache reset.");
    }

    /**
     * Resets DB schema.
     */
    private void resetSchema() {
        ApplicationContext ctx = embeddedJetty.getApplicationContext();
        DataSource dataSource = ctx.getBean(DataSource.class);
        @SuppressWarnings("unchecked")
        List<Resource> databaseScripts = (List<Resource>) ctx.getBean("databaseScriptsList");
        
        Connection con = null;
        ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator();

        try {
            con = dataSource.getConnection();

            resourceDatabasePopulator.setScripts(databaseScripts.toArray(new Resource[0]));   

            resourceDatabasePopulator.populate(con);
        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        } finally {
            try { con.close(); } catch (Exception e) {}
        }
    }

    /**
     * Reset cache.
     */
    private void resetCache() {
        ApplicationContext ctx = embeddedJetty.getApplicationContext();
        EntityManagerFactory entityManagerFactory = ctx.getBean(EntityManagerFactory.class);
        
        Cache cache = entityManagerFactory.getCache();
        
        if (cache != null) {
            cache.evictAll();
        }  
    }
    
}
                    
                

The AbstractPersistenceFindControllerTest provides an abstract base for testing a PersistenceFindMarshallingService.

Example 8. AbstractPersistenceFindControllerTest

                    
public abstract class AbstractPersistenceFindControllerTest<R extends EntityResponseResult, FR extends EntityFindResponseResult, S extends PkEntityBase> 
        extends AbstractRestControllerTest {

    protected final Logger logger = LoggerFactory.getLogger(getClass());

    protected final int id;
    protected final long expectedCount;

    public AbstractPersistenceFindControllerTest(int id, long expectedCount) {
        this.id = id;
        this.expectedCount = expectedCount;
    }
    
    /**
     * Gets find client.
     */
    protected abstract PersistenceFindMarshallingService<R, FR> getFindClient();

    /**
     * Tests if record is valid.
     */
    protected void verifyRecord(S record) {
        verifyRecord(record, false);
    }

    /**
     * Tests if record is valid and can specify whether or not it was a save.
     */
    protected abstract void verifyRecord(S record, boolean save);

    @Test
    @SuppressWarnings("unchecked")
    public void testFindById() {
        R response = getFindClient().findById(id);
        
        assertNotNull("Response is null.", response);

        verifyRecord((S) response.getResults());
    }

    @Test
    @SuppressWarnings("unchecked")
    public void testPaginatedFind() {
        int page = 0;
        int pageSize = 2;
        
        FR response = getFindClient().find(page, pageSize);
        assertNotNull("Response is null.", response);
        
        assertEquals("count", expectedCount, response.getCount());
        
        assertNotNull("Response results is null.", response.getResults());
        verifyRecord((S) response.getResults().get(0));
    }

    @Test
    @SuppressWarnings("unchecked")
    public void testFind() {
        FR response = getFindClient().find();
        assertNotNull("Response is null.", response);

        assertEquals("count", expectedCount, response.getCount());
        
        assertNotNull("Response results is null.", response.getResults());
        verifyRecord((S) response.getResults().get(0));
    }

    /**
     * Tests if audit info is valid.
     */
    protected void verifyAuditInfo(DateTime lastUpdated, String lastUpdateUser,
                                   DateTime created, String createUser) {
        DateTime now = DateTime.now();
        
        assertNotNull("'lastUpdated' is null", lastUpdated);
        assertNotNull("'lastUpdateUser' is null", lastUpdateUser);
        assertNotNull("'created' is null", created);
        assertNotNull("'createUser' is null", createUser);
        
        assertTrue("'lastUpdated' should be before now.", (lastUpdated.isBefore(now)));
        assertTrue("'created' should be before now.", (created.isBefore(now)));
    }

}
                    
                

The AbstractPersistenceControllerTest provides an abstract base for testing a PersistenceMarshallingService.

Example 9. AbstractPersistenceControllerTest

                    
public abstract class AbstractPersistenceControllerTest<R extends EntityResponseResult, FR extends EntityFindResponseResult, S extends PkEntityBase> 
        extends AbstractPersistenceFindControllerTest<R, FR, S> {

    protected final Logger logger = LoggerFactory.getLogger(getClass());

    public AbstractPersistenceControllerTest(int id, long expectedCount) {
        super(id, expectedCount);
    }

    /**
     * Gets find client.
     */
    protected PersistenceFindMarshallingService<R, FR> getFindClient() {
        return getClient();
    }

    /**
     * Gets client.
     */
    protected abstract PersistenceMarshallingService<R, FR, S> getClient();

    /**
     * Create save request.
     */
    protected abstract S createSaveRequest();

    @Test
    @SuppressWarnings("unchecked")
    public void testSave() {
        S request = createSaveRequest();
        
        R response = getClient().save(request);
        
        assertNotNull("Response is null.", response);
        
        verifyRecord((S) response.getResults(), true);

        int expectedCount = 1;
        assertEquals("messageList.size", expectedCount, response.getMessageList().size());

        logger.info(response.getMessageList().get(0).getMessage());
    }

    @Test
    public void testDeletePk() {
        ResponseResult response = getClient().delete(id);
        
        assertNotNull("Response is null.", response);

        int expectedCount = 1;
        assertEquals("messageList.size", expectedCount, response.getMessageList().size());

        logger.info(response.getMessageList().get(0).getMessage());
    }

    /**
     * Tests if primary key is valid.
     */
    protected void verifyPrimaryKey(int id, boolean save) {
        if (!save) {
            assertEquals("'id'", id, this.id);
        } else {
            assertTrue("Primary key should be greater than zero.", (id > 0));
        }
    }

}
                    
                

Code Example

Since all of the main testing for the PersistenceMarshallingService client is in the parent classes, just the constructor and a few methods need to be implemented. The constructor takes the primary key used by find tests and the expected count for retrieving all records. The methods implemented are getClient() to return this test's client, the request to be saved, and a verification method.

Example 10. PersonControllerTest

                    
public class PersonControllerTest extends AbstractPersistenceControllerTest<PersonResponse, PersonFindResponse, Person> {

    @Autowired
    private PersonClient client = null;
    
    public PersonControllerTest() {
        super(1, 3);
    }

    @Override
    protected PersistenceMarshallingService<PersonResponse, PersonFindResponse, Person> getClient() {
        return client;
    }

    @Override
    protected Person createSaveRequest() {
        return new Person().withFirstName(FIRST_NAME).withLastName(LAST_NAME);
    }

    @Override
    protected void verifyRecord(Person record, boolean save) {
        assertNotNull("Result is null.", record);
        
        verifyPrimaryKey(record.getId(), save);
        
        assertEquals("'firstName'", FIRST_NAME, record.getFirstName());
        assertEquals("'lastName'", LAST_NAME, record.getLastName());
        
        verifyAuditInfo(record.getLastUpdated(), record.getLastName(), record.getCreated(), record.getCreateUser());

        logger.debug("id=" + record.getId() + 
                     "  firstName=" + record.getFirstName() + 
                     "  lastName=" + record.getLastName() +
                     "  lastUpdated=" + record.getLastUpdated() +
                     "  lastUpdateUser=" + record.getLastUpdateUser() +
                     "  created=" + record.getCreated() +
                     "  createUser=" + record.getCreateUser());
    }
    
}