<?xml version="1.0"?>

<!--
 * ========================================================================
 * 
 * Copyright 2001-2005 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * ========================================================================
-->

<document id="howto_testcase_servlet">

  <properties>
    <title>ServletTestCase Principles</title>
  </properties>

  <body>

    <section title="When to use?">

      <p>
        Your test case class should extend <code>ServletTestCase</code>
        whenever you are unit testing:
      </p>
      <ul>
        <li>
          Servlets,
        </li>
        <li>
          Any java code that uses Servlet API objects
          (<code>HttpServletRequest</code>, ...)
        </li>
      </ul>

    </section>

    <section title="Provided Implicit Objects">

      <p>
        Cactus automatically initializes the implicit objects for you and
        they are made available to your <code>setUp()</code>,
        <code>testXXX()</code> and <code>tearDown()</code> methods as
        instance variables of the <code>ServletTestCase</code> class (and thus
        as instance variables of your test case class as it extends
        <code>ServletTestCase</code>).
      </p>
      <note>
        You may ask yourself how Cactus initializes these objects. The
        mechanism is described in the <link href="site:how_it_works">How it
        works</link> guide.
      </note>

      <p>
        The provided implicit objects are:
      </p>

      <anchor id="servlet_request"/>
      <section title="request">

        <table>
          <tr>
            <td>
              Instance variable name
            </td>
            <td>
              <strong><code>request</code></strong>
            </td>
          </tr>
          <tr>
            <td>
              Class name
            </td>
            <td>
              <code>org.apache.cactus.server.HttpServletRequestWrapper
              </code>, which inherits from
              <code>javax.servlet.http.HttpServletRequest</code>
            </td>
          </tr>
        </table>

        <p>
          Cactus wraps methods of the original HTTP request in order to return
          the HTTP related parameters set up in the <code>beginXXX()</code>
          method. Thus, you will be able to pass this request object to
          your code to test and set the needed parameter in the
          <code>beginXXX()</code> method.
        </p>
        <p>
          For example, if your code under test calls
          <code>getCookies()</code> on the request object that you have
          passed to it, it will return the cookies that you have added to the
          HTTP request in <code>beginXXX()</code> by calling the
          <code>WebRequest.addCookie()</code> method.
        </p>
        <p>
          See the javadoc for the
          <code>org.apache.cactus.WebRequest</code> interface and the
          <code>org.apache.cactus.server.HttpServletRequestWrapper
          </code> class for all details. You should also look at the
          samples provided in the Cactus distribution.
        </p>

        <section title="Additional methods">

          <p>
            Cactus provides some additional methods to ease writing tests
            (see the javadoc for full details). These methods are provided
            because it is not easy (if not downright impossible in some
            cases) to simulate them with real configuration data:
          </p>
          <ul>
            <li>
              <code>setRemoteIPAddress()</code>: sets the remote IP address
              that will be returned by <code>getRemoteIPAddress()</code>,
            </li>
            <li>
              <code>setRemoteHostName()</code>: sets the remote Host name
              that will be returned by <code>getRemoteHostName()</code>,
            </li>
            <li>
              <code>setRemoteUser()</code>: sets the remote user name
              that will be returned by <code>getRemoteUser()</code>.
            </li>
          </ul>

        </section>

      </section>

      <anchor id="servlet_response"/>
      <section title="response">

        <table>
          <tr>
            <td>
              Instance variable name
            </td>
            <td>
              <strong><code>response</code></strong>
            </td>
          </tr>
          <tr>
            <td>
              Class name
            </td>
            <td>
              <code>javax.servlet.http.HttpServletResponse</code>
            </td>
          </tr>
        </table>

        <note>
          Cactus does not wrap the response.
        </note>

      </section>

      <anchor id="servlet_config"/>
      <section title="config">

        <table>
          <tr>
            <td>
              Instance variable name
            </td>
            <td>
              <strong><code>config</code></strong>
            </td>
          </tr>
          <tr>
            <td>
              Class name
            </td>
            <td>
              <code>org.apache.cactus.server.ServletConfigWrapper
              </code>, which inherits from
              <code>javax.servlet.ServletConfig</code>
            </td>
          </tr>
        </table>

        <p>
          Cactus wraps the original Servlet Config for two reasons:
        </p>
        <ul>
          <li>
            In order to provide additional methods. For example, it is
            possible to initialise parameters without setting them in
            <code>web.xml</code>, etc...,
          </li>
          <li>
            So that it can return a wrapped Servlet Context instead of the
            original one. This is because the Servlet Context is used
            to perform forwards and includes and we need to pass to these
            methods the original request and response. As we have wrapped
            the request, we need to wrap the Servlet Context to pass the
            original request (and not the wrapped one).
          </li>
        </ul>
        <p>
          The <code>config</code> implicit object will contain all
          initialisation parameters defined in <code>web.xml</code>
          under the Servlet Redirector servlet definition.
        </p>
        <p>
          See the javadoc for the
          <code>org.apache.cactus.server.ServletConfigWrapper</code>
          class for all details. You should also look at the
          samples provided in the Cactus distribution.
        </p>

        <section title="Additional methods">

          <p>
            Additional methods provided:
          </p>
          <ul>
            <li>
              <code>setInitParameter()</code>: sets an initialisation
              parameter (as if it has been defined in the <code>web.xml</code>
              file),
            </li>
            <li>
              <code>setServletName()</code>: sets the Servlet name that will be
              returned by <code>getServletName()</code> (if not set, the
              Cactus Servlet redirector name will be returned).
            </li>
          </ul>

        </section>

      </section>

      <anchor id="servlet_context"/>
      <section title="ServletContextWrapper">

        <p>
          This is not an implicit object per see (as it is not accessible as
          an instance variable). It is available by calling
          <code>config.getServletContext()</code>.
        </p>
        <p>
          However, Cactus wraps the <code>ServletContext</code> in a
          <code>ServletContextWrapper</code> in order to take into account
          simulated URLs and provide additional methods to help write tests.
        </p>

        <p>
          See the javadoc for the
          <code>org.apache.cactus.server.ServletContextWrapper</code>
          class for all details. You should also look at the
          samples provided in the Cactus distribution.
        </p>

        <section title="Additional methods">

          <p>
            Additional methods provided:
          </p>
          <ul>
            <li>
              <code>getLogs()</code>: returns the text that has been logged
              by calls to <code>ServletContext.log()</code> methods. This is
              a helper method that makes it easy to assert what is logged.
            </li>
            <li>
              <code>setInitParameter()</code>: sets an initialisation
              parameter (as if it has been defined in the <code>web.xml</code>
              file, using the <code>&lt;context-param&gt;</code> element).
            </li>
          </ul>

        </section>

      </section>

      <anchor id="servlet_session"/>
      <section title="session">

        <table>
          <tr>
            <td>
              Instance variable name
            </td>
            <td>
              <strong><code>session</code></strong>
            </td>
          </tr>
          <tr>
            <td>
              Class name
            </td>
            <td>
              <code>javax.servlet.http.HttpSession</code>
            </td>
          </tr>
        </table>

        <note>
          Cactus does not wrap the response.
        </note>

        <p>
          By default, Cactus always creates an HTTP session for your test
          case. It is possible to tell it not to do so by calling the
          <code>WebRequest.setAutomaticSession(false)</code> method (it is
          <code>true</code> by default). This could be useful for cases
          where your code under test verifies branches when
          "<code>request.getSession(false)</code>" is <code>null</code>.
        </p>

      </section>

    </section>

    <section title="Tips and Tricks">

      <section title="Parameter initialisation">

        <p>
          If your code under test make use of any of the servlet methods
          inherited from <code>javax.servlet.GenericServlet</code> (these are
          the <code>log()</code>, <code>getServletConfig()</code>, ...
          methods), then you need to call the <code>init(ServletConfig)</code>
          method of your servlet to initialise its internal
          <code>ServletConfig</code> object.
        </p>
        <p>
          For example:
        </p>

<source><![CDATA[
public void testXXX()
{
    MyServletToTest servlet = new MyServletToTest();
    servlet.init(config);

    // Call a method to test that uses a method inherited from Generic Servlet
    servlet.someMethodToTest();

[...]
}
]]></source>

        <p>
          See the samples provided as part of the Cactus distribution.
        </p>

      </section>

    </section>

    <section title="Sample">

      <p>
        This is a very basic sample intended to give you a flavour of Servlet
        unit testing. Check the distribution samples for extensive
        examples.
      </p>

      <note>
        This example is for Cactus 1.2 and above as it uses the new
        <code>WebRequest</code> and <code>WebResponse</code> objects.
      </note>

<source><![CDATA[
public void beginXXX(WebRequest theRequest)
{
    // Set up HTTP related parameters
    theRequest.setURL("jakarta.apache.org", "/mywebapp", "/test/test.jsp",
        null, null);
    theRequest.addCookie("cookiename", "cookievalue");
}

public void testXXX()
{
    MyServletToTest servlet = new MyServletToTest();
    servlet.init(config);

    // Call method to test
    servlet.methodToTest();

    // Perform some server side asserts
    assertEquals("someValue", session.getAttribute("someAttribute"));
    assertEquals("jakarta.apache.org", request.getServerName());
}

public void endXXX(WebResponse theResponse)
{
    // Asserts the returned HTTP response

    Cookie cookie = theResponse.getCookie("someCookie");
    assertEquals("someValue2", cookie.getValue());

    assertEquals("some content here", theResponse.getText());
}
]]></source>

    </section>

  </body>
</document>
