blob: 0988e9774d2f35705cc63c5e98d49eb806ab5394 [file] [log] [blame]
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="testing.greenpages">
<title>Testing @greenpages@</title>
<section id="testing.greenpages.introduction">
<title>Introduction</title>
<para>
Testing is one of the most important aspects of software development. Without testing it would be difficult
to determine if a piece of code worked properly, changes would have undetected consequences, and the quality
of products would generally be lower.
</para>
<para>
There are two major categories of testing generally recognised today: unit testing
and integration testing. In the context of the
@greenpages@ application, <emphasis>unit testing</emphasis> means testing a single class in isolation from other application code.
This type of testing does not change at all when developing for @webserv@.
</para>
<para>
In our application <emphasis>integration testing</emphasis> means testing an application or
portion of an application with other code. This kind of testing does look a bit different when developing
for @webserv@. In most cases @webserv@ applications are made up of small bundles that consume services through the
OSGi registry. In the following steps a single bundle and the entire @greenpages@
application will be integration tested outside the container.
</para>
</section>
<section id="testing.greenpages.single.bundle">
<title>Single bundle integration testing</title>
<para>
One of the most common forms of integration testing is ensuring that the object relational mapping in an
application is working properly. This kind of testing typically uses a data access object to retrieve data
from a live database. In this step a test case for the <classname>JpaDirectory</classname>
class is created.
</para>
<para>
Before proceeding, stop any @webserv@ instance that was previously running.
</para>
<para>
Open the <classname>greenpages.jpa.JpaDirectorySpringContextTests</classname> class in the
<filename>src/test/java</filename> source folder of the <literal>greenpages.jpa</literal> project. This
class contains a method that uses <emphasis>JUnit</emphasis> to test that a search completes
correctly. Rather than instantiate
this class directly in the test, the Spring Test Framework is used to instantiate and inject a
<classname>JpaDirectory</classname> based on the <literal>META-INF/spring/module-context.xml</literal> file.
</para>
<para>
Add Spring Test Framework declarations to the test class. These declarations run the test with the
<classname>SpringJunit4ClassRunner</classname> and configure the test with the
<literal>classpath:/META-INF/spring/module-context.xml</literal> file:
<programlisting language="java"><![CDATA[@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:/META-INF/spring/module-context.xml")
@TestExecutionListeners(value = DependencyInjectionTestExecutionListener.class)
public class JpaDirectorySpringContextTests {
…]]>
</programlisting>
Use Eclipse to suggest the necessary imports until there are no errors.
</para>
<para>
When this configuration is complete, click on the <emphasis>Run</emphasis> drop-down menu and select
<emphasis>Run Configurations…</emphasis>. In the the dialog that opens select
<menuchoice><guimenu>JUnit</guimenu><guimenuitem>JpaDirectorySpringContextTests</guimenuitem></menuchoice>
and press <emphasis>Run</emphasis>.
<mediaobject>
<imageobject role="fo">
<imagedata fileref="images/testing-greenpages/jpa-test-runner.png" format="PNG" align="center" width="18cm"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="images/testing-greenpages/jpa-test-runner.png" format="PNG" align="center"/>
</imageobject>
</mediaobject>
</para>
<para>
This test run will fail because there is no <interfacename>DataSource</interfacename> bean to be injected;
it is typically sourced from the OSGi service registry at runtime:
<programlisting><![CDATA[Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No bean named 'dataSource' is defined]]>
</programlisting>
The next step will correct this error.
</para>
</section>
<section id="testing.greenpages.contributing.osgi">
<title>Contributing OSGi sourced dependencies</title>
<para>
In the previous step the <classname>JpaDirectorySpringContextTests</classname> test failed because it did
not have a <interfacename>DataSource</interfacename> to be injected. In this step, an
<quote>in-process</quote> database is instantiated and populated with data for testing.
</para>
<para>
Open the <filename>test-context.xml</filename> file in the
<literal>src/test/resources</literal> <filename>META-INF/spring</filename> folder.
In this file, define two beans; a
<interfacename>DataSource</interfacename> and a <classname>TestDataPopulator</classname>.
These two beans
will provide a test <interfacename>DataSource</interfacename> complete with test data.
<programlisting language="xml"><![CDATA[ <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
p:driverClassName="org.h2.Driver" p:url="jdbc:h2:.~/greenpages-db/greenpages"
p:username="greenpages" p:password="pass" init-method="createDataSource"
destroy-method="close" />
<bean class="greenpages.jpa.TestDataPopulator" init-method="populate">
<constructor-arg ref="dataSource" />
<constructor-arg value="file:../../db/db.sql" />
</bean>]]>
</programlisting>
</para>
<para>
Open the <classname>JpaDirectorySpringContextTests</classname> class and update the
<interfacename>ContextConfiguration</interfacename> annotation to point at both the
<filename>module-context.xml</filename> file and the <filename>test-context.xml</filename> file:
<programlisting language="java"><![CDATA[@ContextConfiguration(locations = {
"classpath:/META-INF/spring/module-context.xml",
"classpath:/META-INF/spring/test-context.xml" })]]>
</programlisting>
</para>
<para>
Once again use the <literal>JpaDirectorySpringContextTests</literal> JUnit profile to run the test class.
Now that there is a <interfacename>DataSource</interfacename> being contributed, the test will pass.
If the test fails, check that the H2 database server is still running in the background.
</para>
<para>
Proceed to the next step.
</para>
</section>
<section id="testing.greenpages.application">
<title>Multi bundle integration testing</title>
<para>
Earlier a single bundle was integration tested by providing a test implementation of its
<interfacename>DataSource</interfacename> dependency.
When integration testing it is often a good idea to
test the entire application outside of the container.
In this step a test case for the
entire @greenpages@ application is created, starting with the <classname>@greenpages@Controller</classname> class
and descending all the way to a database.
It would be sensible to create this in a separate test bundle
but as one of the bundles involved here is a web bundle the tests will have to go in there.
</para>
<para>
Since this project will be testing the @greenpages@ application as a whole, it needs to depend on the bundles
that make up the application.
Open the <filename>pom.xml</filename> file for the <literal>greenpages.web</literal>
project and add a dependency declaration for the <literal>greenpages.jpa</literal> bundle:
<programlisting language="xml"><![CDATA[ <dependency>
<groupId>com.springsource.dmserver</groupId>
<artifactId>greenpages.jpa</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>]]>
</programlisting>
noting that the scope is <literal>test</literal>.
</para>
<para>
Open the <classname>@greenpages@SpringContextTests</classname> class
and add the Spring Test Framework declarations.
These declarations should run the test with the
<classname>SpringJunit4ClassRunner</classname> and configure the test with the
<literal>classpath*:/META-INF/spring/module-context.xml</literal>,
<literal>file:src/main/webapp/WEB-INF/greenpages-servlet.xml</literal> and
<literal>classpath:/META-INF/spring/test-context.xml</literal> files. Note the use of
<literal>classpath*:</literal> with respect to the <literal>module-context.xml</literal> path.
This will
cause Spring to look for files that match that path in all of the bundles on the classpath meaning that all
the application beans will be instantiated.
Also, as we do not want the <literal>WEB-INF</literal> folder
on the classpath we must reference the servlet context for @greenpages@ with a full file path:
<programlisting language="java"><![CDATA[@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
"classpath*:/META-INF/spring/module-context.xml",
"file:src/main/webapp/WEB-INF/greenpages-servlet.xml",
"classpath:/META-INF/spring/test-context.xml" })
@TestExecutionListeners(value = DependencyInjectionTestExecutionListener.class)
public class @greenpages@SpringContextTests {
…]]>
</programlisting>
It may be necessary to click on Update <literal>MANIFEST.MF</literal> on the template overview pane
and <emphasis>Update Dependencies</emphasis> from the <emphasis>Maven</emphasis> menu,
before Eclipse will suggest appropriate imports here.
</para>
<para>
When this configuration is complete, click on the <emphasis>Run</emphasis> drop-down and select
<emphasis>Run Configurations…</emphasis>.
In the the dialog that opens select
<menuchoice><guimenu>JUnit</guimenu><guimenuitem>@greenpages@SpringContextTests</guimenuitem></menuchoice>
and press <emphasis>Run</emphasis>;
<mediaobject>
<imageobject role="fo">
<imagedata fileref="images/testing-greenpages/integration-test-runner.png" format="PNG" align="center" width="18cm"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="images/testing-greenpages/integration-test-runner.png" format="PNG" align="center"/>
</imageobject>
</mediaobject>
</para>
<para>
When this test is run, Spring creates an <interfacename>ApplicationContext</interfacename> that is built
from the <filename>module-context.xml</filename> configuration files from all of the bundles.
Because of
this all of the internal dependencies are satisfied by the beans created directly by the bundles.
</para>
<para>
The test should pass. If it doesn't, try the usual Eclipse dance steps of opening and closing the project and doing a clean rebuild to clear the problem.
</para>
<para>
There are warnings output by this test concerning <literal>log4j</literal>:
<programlisting>log4j:WARN No appenders could be found for logger
(org.springframework.test.context.junit4.SpringJUnit4ClassRunner).
log4j:WARN Please initialize the log4j system properly.
</programlisting>
These warnings are benign, and do not influence the tests in any way.
</para>
<para>
The next chapter constructs an automated build system that might be used to build @greenpages@
(and run its tests) outside of an interactive development environment.
</para>
</section>
</chapter>