| <?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> |