Author: craigmcc
Date: Sat Oct 29 17:45:51 2005
New Revision: 329501
URL: http://svn.apache.org/viewcvs?rev=329501&view=rev
Log:
Flesh out the description of the Shale Test Framework feature, and use the
<source> markup element that Maven supports to create boxes around the
code examples.
Modified:
struts/shale/trunk/xdocs/features.xml
Modified: struts/shale/trunk/xdocs/features.xml
URL:
http://svn.apache.org/viewcvs/struts/shale/trunk/xdocs/features.xml?rev=329501&r1=329500&r2=329501&view=diff
==============================================================================
--- struts/shale/trunk/xdocs/features.xml (original)
+++ struts/shale/trunk/xdocs/features.xml Sat Oct 29 17:45:51 2005
@@ -342,7 +342,7 @@
<li>Define your dialogs in an XML document, conventionally named
<code>/WEB-INF/dialog-config.xml</code>, that conforms to the
required DTD, which defines all the state transitions:
- <blockquote><pre>
+<source>
<!DOCTYPE dialogs PUBLIC
"-//Apache Software Foundation//DTD Shale Dialog Configuration 1.0//EN"
"http://struts.apache.org/dtds/shale-dialog-config_1_0.dtd">
@@ -360,18 +360,18 @@
...
</dialogs>
-</pre></blockquote></li>
+</source></li>
<li>If you have more than one dialog configuration file, or you have
defined your only dialog configuration file as a web application
resource with a name different than the one described above, use
a context initiaization parameter to define a comma-delimited list
of context-relative paths to configuration resources to be loaded:
- <blockquote><pre>
+<source>
<context-param>
<param-name>org.apache.shale.dialog.CONFIGURATION</param-name>
<param-value>/WEB-INF/foo.xml,/WEB-INF/bar.xml</param-value>
</context-param>
-</pre></blockquote></li>
+</source></li>
<li>In addition to the dialog configuration resources defined by this
context initialization parameter, a resource named
<code>/WEB-INF/dialog-config.xml</code> will be automatically
@@ -463,7 +463,7 @@
</ul>
<p>Here's an example:</p>
-<pre>
+<source>
<%@ taglib uri="http://struts.apache.org/shale/core" prefix="s" %>
...
<h:form onsubmit="validateForm(this)">
@@ -493,7 +493,7 @@
<s:validatorScript functionName="validateForm"/>
</form>
...
-</pre>
+</source>
<p>In the preceding example, we've attached three Commons validators
to a single
JSF input component. To pass validation, the field must
have a value that
starts with a number between 4 and 6 inclusive and
that value must be a valid
@@ -587,49 +587,49 @@
environment entry to define whether your application is running in
debug mode or not, declared in <code>web.xml</code> like this:</p>
- <blockquote><pre>
+<source>
<env-entry>
<description>Flag indicating whether we run in debug
mode.</description>
<env-entry-name>debugMode</env-entry-name>
<env-entry-value>false</env-entry-value>
<env-entry-type>java.lang.Boolean<env-entry-type>
</env-entry>
-</pre></blockquote>
+</source>
<p>Now, assume you have a status message that you only want to have
displayed when debug mode is enabled. You can bind the
<code>rendered</code> property of the component to this environment
entry value:</p>
- <blockquote><pre>
+<source>
<h:outputText ... rendered="#{jndi.debugMode}" .../>
-</pre></blockquote>
+</source>
<p><strong>(2) Programmatic Resource Access</strong></p>
<p>Assume you have a data source reference (discussed in the
introduction)
defined in your <code>web.xml</code> like this:</p>
- <blockquote><pre>
+<source>
<resource-ref>
<description>Customer Database</description>
<res-ref-name>jdbc/CustomerDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
-</pre></blockquote>
+</source>
<p>You can acquire a <code>java.sql.Connection</code> from this data
source with code like the following (note that the convenience base
class <code>BaseViewController</code> contains a <code>getBean()</code>
method that substantially reduces the amount of code needed):</p>
- <blockquote><pre>
+<source>
FacesContext context = FacesContext.getCurrentInstance();
ValueBinding vb =
context.getApplication().createValueBinding("#{jndi['jdbc/CustomerDB'].connection}");
Connection conn = (Connection) vb.getValue(context);
-</pre></blockquote>
+</source>
<p>This works by first retrieving the JNDI-configured data source
instance, and then calling its <code>getConnection()</code> method.
@@ -674,9 +674,9 @@
for inheritance relationships. Clay's approach is unique. The
granularity is targeted at the declaration of JSF components versus
composition of JSP fragments.
- <blockquote><pre>
+<source>
<clay:clay id="address" jsfid="addressPanel"/>
-</pre></blockquote></li>
+</source></li>
<li>The subtree can be formed using a Tapestry like HTML layout. HTML
elements are bound to corresponding JSF components using a
<code>jsfid</code> attribute. This attribute binds the HTML mock-up
@@ -688,9 +688,9 @@
standard <code>verbatim</code> (<code>ouputText</code>) component.
This combines the first option's component definitions
with the flexibility of using HTML for layout.<br/>
- <blockquote><pre>
+<source>
<clay:clay id="address" jsfid="address.html"/>
-</pre></blockquote></li>
+</source></li>
<li>The subtree can also be defined at runtime. The Clay component
provides a postback validation method event,
<code>shapeValidator</code>, that can be bound to the associated
@@ -700,9 +700,9 @@
graph of objects used by the Clay component to build the
subcomponent tree. The object's graph is representative of
the first two declarative approaches.
- <blockquote><pre>
+<source>
<clay:clay id="address" jsfid="RUNTIME"
shapeValidator="#{fullAddress.createSubtree}"/>
-</pre></blockquote></li>
+</source></li>
</ol>
</subsection>
@@ -723,19 +723,21 @@
"top-level" elements can be the root of a clay subtree. The
<code>componentType</code> attribute defines the association
to the face's resource. The <code>jsfid</code> attribute is a
- logical unique identifier.
- <blockquote><pre>
+ logical unique identifier.</p>
+
+<source>
<component jsfid="outputText"
componentType="javax.faces.HtmlOutputText"/>
<component jsfid="validateLongRange"
componentType="javax.faces.LongRange"/>
-</pre></blockquote></p>
+</source>
<p>A <code>component</code> can extend another component, thereby
inheriting <code>attributes</code> and contained <code>element</code>
nodes from the parent component. This is accomplished by using
the <code>extends</code> attribute. The value of the
<code>extends</code> attribute should be the <code>jsfid</code> of
- the parent <code>component</code> definition.
- <blockquote><pre>
+ the parent <code>component</code> definition.</p>
+
+<source>
<component jsfid="baseLabel" extends="outputLabel" allowBody="false">
<attributes>
<set name="style" value="color:blue"/>
@@ -747,14 +749,15 @@
<set name="for" value="city"/>
</attributes>
</component>
-</pre></blockquote></p>
+</source>
<p>A <code>component</code> node has an attribute container. This is
a generic container to hold all <code>attributes</code> that would
be represented by associated JSF/JSP Tags. Attributes are added or
overridden in inheritance using the attribute <code>name</code> as the
- unique identifier.
- <blockquote><pre>
+ unique identifier.</p>
+
+<source>
<component jsfid="addressPanel" extends="panelGrid">
<attributes><br/>
<set name="columns" value="2" />
@@ -768,7 +771,7 @@
</attributes>
<element renderId="3" jsfid="street1Message"/>
</component>
-</pre></blockquote></p>
+</source>
<p>The <code>element</code> node is the composition glue. Components
are uniquely defined by a <code>renderId</code> attribute. This
@@ -777,8 +780,9 @@
acts as the "method signature" for the <code>element</code> when
resolving inheritance. This means components can extend other
components by overriding or extending elements based on the
- <code>renderId</code> of the first level of child components.
- <blockquote><pre>
+ <code>renderId</code> of the first level of child components.</p>
+
+<source>
<component jsfid="ssnColumn" extends="column" id="ssn">
<element renderId="1" jsfid="outputText" facetName="header">
<attributes>
@@ -807,7 +811,7 @@
<element renderId="0" jsfid="ssnColumn"/>
<element renderId="3" jsfid="birthDateColumn"/>
</component>
-</pre></blockquote></p>
+</source>
<p>Clay also allows reuse of view fragments bound to different logical
managed bean names. Managed bean names in Shale will most likely
@@ -818,8 +822,9 @@
"managed-bean-name" is replaced with the value of the Clay
<code>managedBeanName</code> property. This preprocessing is
performed prior to applying the meta attribute values to the target
- JavaServer Faces component's properties.<br/>
- <blockquote><pre>
+ JavaServer Faces component's properties.</p>
+
+<source>
<clay:clay id="saveResidential" managedBeanName="residentialAddress"
jsfid="saveCommand"/>
<clay:clay id="saveBusiness" managedBeanName="businessAddress"
jsfid="saveCommand"/>
<component jsfid="saveCommand" extends="commandButton">
@@ -830,7 +835,7 @@
</attributes>
<actionListener jsfid="logNavigationActionListener"/>
</component>
-</pre></blockquote></p>
+</source>
</subsection>
@@ -839,7 +844,269 @@
<section name="Shale Test Framework">
<a name="test"/>
- <p>FIXME - Describe Test Framework feature.</p>
+ <a name="test-introduction"/>
+ <subsection name="Introduction">
+
+ <p>Modern application development processes have embraced the idea of
+ <em>unit testing</em> as an integral step in creating high quality
+ software. In the Java world, a popular framework for building and
+ executing such unit tests is the <a href="http://junit.org">JUnit</a>
+ framework. It is straightforward for an application developer to
+ create a corresponding <em>test case</em> class for each class in the
+ application itsef, and ensure that the tests contained in the test case
+ get executed as part of the normal application build process.</p>
+
+ <p>One of the tenets of unit testing is that a test case should focus
+ <em>only</em> on the methods of the class under test, in isolation from
+ related application classes, or APIs provided by any container that the
+ class under test might be installed into at runtime. But, how do you
+ test an application class that has dependencies on such APIs (such as
+ depending on the Servlet API to provide an
<code>HttpServletRequest</code>
+ object representing an incoming HTTP request?</p>
+
+ <p>A popular answer to this dilemna is to utilize a library of
+ <em>mock objects</em> -- classes that implement and emulate the
container
+ APIs, but still run in the isolated environment of a JUnit test case.
+ Shale provides mock object implementations for its own features, as
+ well as features of the underlying container (Servlet
+ and JavaServer Faces) environment. In addition, convenient base
classes
+ are provided to make it very easy to build your own test cases
utilizing
+ these mock objects. This library is used to create unit tests for
Shale
+ components itself, but it is primarily focused on making it easy to
+ build unit tests for application classes such as
+ <code>ViewController</code>s.</p>
+
+ </subsection>
+
+ <a name="test-services"/>
+ <subsection name="Provided Services">
+
+ <p>The Shale Test Framework provides mock object libraries, plus base
+ classes for creating your own JUnit <code>TestCase</code>s.</p>
+
+ <p>Mock objects are provided in package
<code>org.apache.shale.test.mock</code>
+ for the following container APIs:</p>
+ <ul>
+ <li>JavaServer Faces</li>
+ <li>Servlet</li>
+ </ul>
+ <p>These mock object classes implement the majority of the
functionality
+ defined by the container API Javadocs (although some methods currently
+ throw <code>UnsupportedOperationException</code>). In addition, many
+ of these classes support public methods, outside of the defined API,
+ for configuring the object in a test environment. For example,
+ <code>MockServletContext</code> includes
<code>addInitParameter()</code>
+ and <code>setDocumentRoot()</code> methods, to add context
initialization
+ parameters to the set returned via <code>getInitParameter()</code> and
+ <code>getInitParameterNames()</code>, and to establish the base
directory
+ for resolving servlet context resources, respectively.</p>
+
+ <p>The <code>org.apache.shale.test.base</code> package contains
abstract
+ base classes that wire together instances of the various container API
+ mock objects, in a manner similar to the way they would become
available
+ at runtime. The following base classes are available:</p>
+ <ul>
+ <li><code>AbstractJsfTestCase</code> - Base class for unit tests that
+ require Servlet and JavaServer Faces objects to be available.</li>
+ <li><code>AbstractViewControllerTestCase</code> - Extension of
+ <code>AbstractJsfTestCase</code> that also provides convenient
+ utility methods needed to test common scenarios in unit tests for
+ <code>ViewController</code> implementation classes.</li>
+ </ul>
+
+ <p>If you use one of these base classes, the <code>setUp()</code>
method
+ found there will initialize a set of <code>protected</code> instance
+ variables for the container-managed objects you might need to access.
+ The set of initialized variables includes (variable name and type):</p>
+ <ul>
+ <li><code>application</code> (<code>MockApplication</code>)</li>
+ <li><code>config</code> (<code>MockServletConfig</code>)</li>
+ <li><code>externalContext</code>
(<code>MockExternalContext</code>)</li>
+ <li><code>facesContext</code> (<code>MockFacesContext</code>)</li>
+ <li><code>lifecycle</code> (<code>MockLifecycle</code>)</li>
+ <li><code>request</code> (<code>MockHttpServletRequest</code>)</li>
+ <li><code>response</code> (<code>MockHttpServletResonse</code>)</li>
+ <li><code>servletContext</code> (<code>MockServletContext</code>)</li>
+ <li><code>session</code> (<code>MockHttpSession</code>)</li>
+ </ul>
+
+ </subsection>
+
+ <a name="test-using"/>
+ <subsection name="Using View Controller">
+
+ <p>The most common scenario for using the Test Framework is to
construct
+ test cases for <code>ViewController</code> implementation classes.
+ Because the runtime environment of a <code>ViewController</code> is
+ quite constrained, it is easy to construct isolated unit tests that
+ exercise the methods exposed by a <code>ViewController</code> class.
+ The <em>Shale Use Cases</em> web application (included in the
distribution)
+ contains many examples of such test cases, in the <code>src/test</code>
+ directory. We will use
<code>org.apache.shale.usecases.locale.SelectTestCase</code>
+ (which tests the <code>org.apache.shale.usecases.locale.Select</code>
+ implementation) as an example of how such a test case can be
constructed.</p>
+
+ <ol>
+ <li>Create a new Java class <code>SelectTestCase</code>, in a package
+ directory (typically under <code>src/test</code> in your project)
+ that is the same as the package directory for the class you will be
+ testing. This allows your test case to access package private and
+ protected variables and methods in the class being tested.</li>
+ <li>Make sure that the package declaration matches that of the class to
+ be tested (in this case,
<code>org.apache.shale.usecases.locale</code>.</li>
+ <li>Declare your class to extend
<code>AbstractViewControllerTestCase</code>
+ (or, if you are not testing a <code>ViewController</code>
implementation,
+ extend <code>AbstractJsfTestCase</code>):
+<source>
+public class SelectTestCase extends AbstractViewControllerTestCase {
+ ...
+}
+</source></li>
+ <li>Create a constructor that takes a <code>String</code> parameter,
and
+ passes it to the superclass constructor:
+<source>
+public SelectTestCase(String name) {
+ super(name);
+}
+</source></li>
+ <li>Create a <code>setUp()</code> method and <strong>be sure</strong>
+ to call <code>super.setUp()</code> at the beginning. This method
+ will be called by JUnit immediately before it executes each
+ test method.
+<source>
+public void setUp() {
+ super.setUp();
+ // Customization will go here
+}
+</source></li>
+ <li>After the call to the superclass <code>setUp()</code> method,
+ perform any other initialization required to execute the tests
+ in this test case. In our example case, a configuration method
+ on the <code>MockApplication</code> instance will be used to
+ define the default and supported <code>Locale</code>s for this
+ set of tests. This corresponds to what would happen at runtime,
+ when the JavaServer Faces initialization process used the contents
+ of the <code>/WEB-INF/faces-config.xml</code> resource to
initialize
+ these values. In addition, we will create a new instance of the
+ <code>Select</code> class to be tested. It is important to create
+ a new instance for each test, to ensure that execution of one test
+ does not get influenced by the leftover property settings from a
+ previous test.
+<source>
+public void setUp() {
+ super.setUp();
+
+ // Configure the supported locales for this application
+ List list = new ArrayList();
+ list.add(new Locale("en"));
+ list.add(new Locale("fr"));
+ list.add(new Locale("de"));
+ list.add(new Locale("es"));
+ application.setSupportedLocales(list);
+
+ // Construct a new ViewController instance
+ vc = new Select();
+
+}
+</source></li>
+ <li>Create a static <code>suite()</code> method that identifies the
+ test methods to be provided by this test case. The simplest
+ approach is to leverage a constructor of the <code>TestSuite</code>
+ class that accepts the <code>Class</code> instance for this test
+ suite:
+<source>
+public static suite() {
+ return new TestSuite(SelectTestCase.class);
+}
+</source></li>
+ <li>Create a <code>tearDown()</code> method that cleans up any custom
+ variables you allocated in your <code>setUp()</code> method, and
+ then calls the <code>super.tearDown()</code> method. This will be
+ called by JUnit after each test is executed.
+<source>
+public void tearDown() {
+ vc = null;
+ super.tearDown();
+}
+</source></li>
+ <li>Declare the custom instance variable(s) that you are setting up
+ in your <code>setUp()</code> method. In this case, we create an
+ instance of the <code>ViewController</code> class to be tested.
+ A new instance will be created (via a call from JUnit to the
+ <code>setUp()</code> method) before each test method is executed.
+<source>
+// The instance to be tested
+Select vc = null;
+</source></li>
+ <li>Create one or more individual test methods (which must be
+ <code>public</code>, return <code>void</code>, take no arguments,
+ and have a method name of the form <code>testXXXX</code>. For
+ advice on how to construct such methods, consult the
+ <a href="http://junit.org/">JUnit Web Site</a>, or any of the
+ large number of resources on the web describing how to use JUnit
+ to build unit tests. The following example tests what happens
+ when the <code>select()</code> method (which is executed when
+ the <em>Go</em> button is pressed), but the value entered is not
+ one of the valid options. <strong>NOTE</strong> that the test
+ method must emulate the runtime calls to the
<code>ViewController</code>
+ event methods, because there is no actual runtime container
+ available to perform these tasks automatically:
+<source>
+// Test behavior of select() with an invalid value
+public void testSelectInvalid() {
+
+ Locale locale = new Locale("en");
+ facesContext.getViewRoot().setLocale(locale);
+ vc.init();
+ vc.preprocess();
+ vc.setLocale("it");
+ String result = vc.select();
+ assertEquals(Select.FAILURE, result);
+ checkMessageCount(1);
+ assertEquals(locale, facesContext.getViewRoot().getLocale());
+
+}
+</source>
+ The test case sets the <code>locale</code> property (which is
+ bound to a dropdown component at runtime, but we are simulating
+ the behavior of Update Model Values here) to an invalid value,
+ then calls the <code>select()</code> method. The test then
+ verifies that the logical outcome returned matches that which
+ is expected (<code>Select.FAILURE</code>), that there was an error
+ message queued to be displayed, and that the <code>locale</code>
+ for the current view was <strong>NOT</strong> actually changed.
+ <br/></li>
+ <li>Finally, integrate the execution of this test case into your
+ build script. Many IDEs will take care of this for you; however,
+ if you are creating an Ant build script by hand, you might find
+ the <code>test</code> target from the Shale Use Cases example
+ a useful starting point. It locates <em>all</em> the test cases
+ related to the entire application, and executes them:
+<source>
+ <target name="test" depends="test.compile"
+ description="Execute unit tests">
+
+ <mkdir dir="${build.home}/test-results"/>
+
+ <echo message="Running unit tests ..."/>
+ <junit printSummary="no" fork="yes"
+ haltonfailure="yes" haltonerror="yes">
+ <classpath refid="test.classpath"/>
+ <formatter type="plain"
+ usefile="false"/>
+ <formatter type="xml"
+ usefile="true"/>
+ <batchtest todir="${build.home}/test-results">
+ <fileset dir="${build.home}/test-classes"
+ includes="org/apache/shale/usecases/*/*TestCase.class"/>
+ </batchtest>
+ </junit>
+
+ </target>
+</source></li>
+ </ol>
+
+ </subsection>
</section>
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]