# Unit Testing Externally Testable Components

Most WebLogic Platform components are externally testable, that is, the test is external to the server and can be invoked from outside the J2EE container.

Sections in this topic include:

- Java Page Flows, Java Server Pages, and Servlets
- Java Web Services
- Enterprise Java Beans

## Java Page Flows (JPF), Java Server Pages (JSP), and Servlets

When testing web sites, your test is essentially simulating a browser requesting a page or a series of pages from the server. Depending on what the site involves, you will need to create the correct HTTP request, including the relevant URL, headers, parameters, cookies, and so forth. Once the server responds, you will need to verify the contents including the headers, the HTML itself, client-side JavaScript, and cookies. There are two general ways to do this.

### Proxy Use

First, you could use a proxy to record the interaction between a normal web browser and the server. After you have gone through the correct sequence of pages and manually verified that the results are correct, you can use the recording to automatically play back the same series of requests and verify that the results are the same. There are a number of packages that use this approach, including MaxQ and QuickTest.

This approach is very quick to generate a set of tests, but is unfortunately very fragile. As soon as you make changes to the page layouts, you run a high risk of breaking your tests. For sites that are have constant layout changes, this means that is likely that you will have to re-record your tests on a regular basis.

### Object Model

A second approach is to use a more programmatic technique by writing a test that builds up an object requesting the HTTP request, hits the server, and gets an object back representing the HTTP response. Although this is more work than using a proxy to record the session, it is less likely to be broken by changes to the HTML layout. Several libraries, including HTTPUnit, HTMLUnit, and jWebUnit all provide APIs to simulate the browser interaction.

### Sample Page Flow

HTMLUnit is an example of the object model approach. Its API provides an object model for the HTML page that the server returns. Each request returns an HtmlPage, which you can then query about its contents. After finding a link in the page, you can simulate a user clicking on it in a browser, which will then return another HtmlPage. Similarly, you can locate forms within the page, find specific fields, set values, and submit to the server.

HTMLUnit uses the Rhino JavaScript engine to let you test script within your pages. However, it’s important to note that it only supports a subset of the JavaScript that most browsers support, and that JavaScript behavior is always at least somewhat browser dependent.

To demonstrate testing a web site, look at the multipleForms page flow from the WebLogic Workshop 8.1 SP2 SamplesApp, which can be found in the code download, in the JPFWebProject project. The portion of the flow the test covers has two text fields. After entering values into them and clicking the submit button, the values are displayed on the subsequent page.

The JUnit test case for this example is {JPFTestProject}/JPFTest.java. It has a main() method as described above so that you can run it by clicking the Start button inside the IDE:

    public void testPageFlow() throws Exception
     {
         WebClient webClient = new WebClient();
         HtmlPage page = (HtmlPage)webClient.getPage(
         new URL( "http://localhost:7001/JPFWebProject/multipleForms/multipleFormsController.jpf") );
         HtmlAnchor anchor = page.getFirstAnchorByText( "Show the JSP with blank form fields." );
         HtmlPage page2 = (HtmlPage)anchor.click();
         HtmlForm form = (HtmlForm)page2.getFormByName( "form1" );
         Iterator elements = form.getAllSubmittableElements().iterator();
         int count = 1;
         while( elements.hasNext() )
    		{
         	HtmlElement element = (HtmlElement)elements.next();
         	assertTrue( element instanceof HtmlTextInput );
         	HtmlTextInput textInput = (HtmlTextInput)element;
         	textInput.setValueAttribute( "Value" + count++ );
         	}
         HtmlPage page3 = (HtmlPage)form.submit();
         String pageText = page3.asText();
         assertTrue( pageText.indexOf( "Field A = \"Value1\"" ) != -1 );
         assertTrue( pageText.indexOf( "Field B = \"Value2\"" ) != -1 );
         }

The test itself is fairly straightforward. It first creates a WebClient object, which corresponds to a web browser. It then issues a request to the server at the page flow’s URL. The test looks for a link with the text “Show the JSP with blank form fields.” It clicks on the link, and in the resulting page locates the form. After iterating through the form elements and giving them values, the test submits the form and looks for those values in the result page.

If you are using NetUI on your site, be warned that the taglibs typically do not generate a very simple name for the form elements. Normally, this does not matter because neither you as a developer nor the end user need to be concerned with the names, but when writing tests you usually need to set specific elements to particular values. You can always find the name for an element by viewing the page’s HTML source in a browser.

## Java Web Services (JWS)

Like HTML-based components, Java Web Services are accessible from outside the server over HTTP. A JWS, though, provides a more stable API through its WSDL, which can make it easier to test because the shape of the input and output XML is less likely to change than the exact HTML that represents the output of a JSP. The XML returned from a web service does not contain the formatting information that makes HTML much more likely to change due to layout tweaks.

You might want to consider testing your web services from the same client platform from which they will be called in production. For example, if you use WebLogic Workshop to implement your web services on your server and always call them from clients written in Microsoft .NET, you may want to write at least some of your tests in .NET to also test the cross-platform interaction.

Assuming that you want to write and run your tests in Java, you’d like to be able to write the tests using strongly typed Java objects instead of crafting an HTTP request and parsing the resulting XML. In 8.1, there are two ways to create such an interface conforming to the JAX-RPC specification.

### Using <clientgen>

WebLogic Server provides a <clientgen> Ant task. You point the task at a WSDL that describes the web service and tell it where to put the resulting Java classes. For documentation on how to use the task, see http://e-docs.bea.com/wls/docs81/webserv/anttasks.html#1080160.

### Using Test Client

You can also use the WebLogic Workshop Test Client to generate the proxy JAR. Just hit the Start button on your JWS file, go to the Overview tab in the Test Client, and click on the Java Proxy button. If you prefer, you can specify the package in which to put the proxy classes otherwise use the default weblogic.jws.proxies.

### Sample Web Service

The JWS that is shown below as an example can be found in {UnitTestingWeb}/jws/JWSToTest.jws. It defines two very simple operations. While not very interesting, they’ll be sufficient for our purposes. You’ll also see that a WSDL was generated from the JWS, which Workshop will automatically keep in sync with the JWS if its interface changes.

public class JWSToTest implements com.bea.jws.WebService

    {
         static final long serialVersionUID = 1L;

     /** @common:operation */
         public int square( int i )
    	 {
         return i * i;
         }

     /** @common:operation */
         public String hello()
    	 {
         return "Hello";
         }
    }

The test for our JWS is {JWSTestProject}/JWSTest.java, which is partially shown here:

    private JWSToTest _proxy;

    public void setUp() throws IOException {
         _proxy = new JWSToTest_Impl();
         }

    public void testJWS() throws Exception {
         assertEquals( 25, _proxy.getJWSToTestSoap().square( 5 ) );
         }

Like the page flow test, it has a main() method so you can run it just by clicking on the Start button. In the setUp() method, which is called before each individual test is run, we create an instance of the JAX-RPC proxy. Then, inside our tests, we can simply pass the relevant arguments to the method and verify that the result is correct.

Since the proxy is generated from the WSDL, it already knows the URL at which to access the web service. You can test a different server running the same web service, like a staging server, by passing a different URL to the proxy. For more information on the JAX-RPC proxies, see http://java.sun.com/xml/jaxrpc/index.jsp.

## Enterprise Java Beans (EJB)

Testing an EJB as an external component is very similar to calling it from any other client. (You can also test an EJB as an internal component. For more information, see below. You’ll need to do the basic set of steps that you are used to with EJBs: (1) look up the home interface in JNDI, (2) create an instance of the bean, and (3) call methods on it.

###
Sample EJB

This sample tests the stateless session bean found at {EJBProject}/ejbpackage/SampleSessionBean.ejb. It defines a single bean method, doSomethingBoring(), which certainly lives up to its name by simply returning 5:

    public class SampleSessionBean extends GenericSessionBean implements SessionBean
    {
         public void ejbCreate() {}

     /** @ejbgen:remote-method */
         public int doSomethingBoring() {
         return 5;
         }
    }

For this sample, the test case is {UnitTestProject}/EJBTest.java, of which a snippet is shown below:

    private SampleSessionHome lookupHome() throws NamingException {
         Context ctx = getInitialContext();
         // Lookup the bean's home using JNDI
         Object home = ctx.lookup("ejb.SampleSessionRemoteHome");
         return (SampleSessionHome) narrow(home, SampleSessionHome.class);
         }

    private Object narrow(Object ref, Class c) {
         return PortableRemoteObject.narrow(ref, c);
         }

    private Context getInitialContext() throws NamingException {
         // Set up the environment properties
         Hashtable h = new Hashtable();
         h.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
         h.put(Context.PROVIDER_URL, "t3://localhost:7001");
    	 return new InitialContext(h);
         }

    public void testEJB() throws Exception {
         assertEquals( 5, lookupHome().create().doSomethingBoring() );
         }

Within our test method, we get the initial context for the server, lookup the home interface in JNDI, narrow it, call the create method, and invoke the bean method. This test, too, can be run using the Start button.

## Related Topics

Unit Testing with JUnit

Launching JUnit

Unit Testing Internally Testable Components