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 in Spring by Example REST Module

                    
@ContextConfiguration({ "classpath:/org/springbyexample/web/mvc/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.
     */
    @Override
    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 in Spring by Example REST Module

                    
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 create valid.
     */
    protected void verifyRecord(S record) {
        verifyRecord(record, false, false);
    }

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

    @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 in Spring by Example REST Module

                    
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.
     */
    @Override
    protected PersistenceFindMarshallingService<R, FR> getFindClient() {
        return getClient();
    }

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

    /**
     * Generate create request.
     */
    protected abstract S generateCreateRequest();

    /**
     * Generate update request.
     */
    protected abstract S generateUpdateRequest(S request);

    /**
     * Generate delete request.
     */
    protected abstract S generateDeleteRequest();

    /**
     * Tests if record valid, update is set to <code>false</code>.
     */
    protected void verifyRecord(S record, boolean save) {
        verifyRecord(record, save, false);
    }

    @Test
    @SuppressWarnings("unchecked")
    public void testCreate() {
        S request = generateCreateRequest();

        R response = getClient().create(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
    @SuppressWarnings("unchecked")
    public void testUpdate() {
        S request = generateCreateRequest();

        R response = getClient().create(request);

        assertNotNull("Response is null.", response);

        verifyRecord((S) response.getResults(), true);

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

        request = generateUpdateRequest((S) response.getResults());
        response = getClient().update(request);

        verifyRecord((S) response.getResults(), true, true);

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

    @Test
    public void testDeletePk() {
        R response = getClient().delete(generateDeleteRequest());

        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 AbstractPersistenceContactControllerTest<PersonResponse, PersonFindResponse, Person> {

    @Autowired
    private final PersonClient client = null;

    public PersonControllerTest() {
        super(1, 3);
    }

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

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

    @Override
    protected Person generateUpdateRequest(Person request) {
        return request.withLastName(NEW_LAST_NAME);
    }

    @Override
    protected Person generateDeleteRequest() {
        return new Person().withId(id);
    }

    @Override
    protected void verifyRecord(Person record, boolean save, boolean update) {
        assertNotNull("Result is null.", record);

        verifyPrimaryKey(record.getId(), save);

        assertEquals("'firstName'", FIRST_NAME, record.getFirstName());

        if (!update) {
            assertEquals("'lastName'", LAST_NAME, record.getLastName());
        } else {
            assertEquals("'lastName'", NEW_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());
    }

}