Sunday, August 3, 2008

A Quick Start Guide to Hatatap

HATATAP

– has evolved from the sound of a north-european trying to pronounce 'http'. Why HTTP? HATATAP is a script based HTTP testing tool. This is built on top of the strong foundation that Metware’s HTTP Unit gives you. Many questions revolve around the head as to when would somebody make use of HTTP Unit and when HATATAP? This article aims at giving the readers an insight into this. Another goal for writing this article is that we don’t find full fledged examples over the web to get started quickly.

Why hatatap?

1. It gives us a strong test framework based on HTTP Unit and also realizes the power of ‘o:xml’ (Object Oriented XML).
2. The easy to code and understand scripts are transformed into ‘o:xml’ before they are executed.
3. The best part is that you could also mix up the hatatap scripts with ‘o:xml’ to build your ideal test script.
4. Another reason to vote for hatatap is that the scripts integrate well with Apache Ant, so that it could be built into any Enterprise level build environment.
5. One may also run the scripts from commandline and have a batch utility written to execute a set of scripts.
6. Hatatap can also be run as a web application where it provides a ready to use Servlet based test framework which just requires us to drop in hatatap scripts under the webapp and run it through your browser.
7. Last but not the least, they could unleash the power of XPATH, which is what I was looking to use in my project.

Note: Above is a generalized listing of key features. There are much more things you could do with hatatap, for which you would have to look over some articles on the web, since they are out of the scope of this document.

We’ll there are a couple of words of caution as well:
1. There are some known bugs related to Xalan and Java that makes hatatap go crazy.
2. For support, though there is a known forum I had a bad experience with it. Though I got over my issues long back, there is no response from the author to my post.
3. Watchout for the ‘.’ Operator. Avoid using ‘.’ To separate variable names or as part of a variable name. This causes the scripts to be dead scripts.

OK, lets try to do a small example and in the process try to learn as much as possible. For the example purpose we would make use of hatatap with Apache Ant.

Get set Go:

1. This example assumes that we have Ant and java installed and ready to use. If not please download and install.
2. Create a working directory to use as the base directory for the sample application. For the sample sake let’s have the directory names ‘hatatap’.
3. Create a 'lib' folder and place the following libraries (.jar)
a. hatatap.jar (The main library which includes the hatatap and object box libraries used for running hatatap tests. You can find this in the hatatap distribution)
b. xalan.jar (Hatatap uses the XSLT engine. We need to provide the TransformerFactory (org.apache.xalan.processor.TransformerFactoryImpl), since the one which comes bundled with java 1.5 and higher has compatibility issues with hatatap. Earlier versions of java were never bundled with Xalan so we have to provide the engine.
c. serializer.jar (Since we are providing the XSLT engine, we also need to provide our XML Serializer)
d. Apart from the libraries, place the hatatap XSD (script.xsd) which was bundled with the hatatap distribution. This will be good to reference in the scripts.
4. Create an ‘input’ directory under ‘hatatap’. Here we would park the hatatap scripts to be executed.
5. Create the build environment. Let’s put together a brief build.properties and a build.xml to compile and run hatatap scripts we will create.

Build.properties

server=http://www.pingdynasty.com

# directory requirements
test_input=input
test_output=output

#log directory
log_dir=log


Above, we have a brief properties file for the build which lists a few properties such as input, output, the server to test with, log directory for any logs generated etc.
Build.xml

<?xml version="1.0" encoding="UTF-8"?>
<project name="hatatap" default="alltests" basedir=".">

<!-- build properties -->
<property file="build.properties"/>
<!--Defines the path where the Hatatap library resides. -->
<path id="lib_path">
<fileset dir="lib">
<include name="*.jar*"/>
</fileset>
</path>
<!-- Hatatap task def -->
<taskdef name="hatatap" classname="com.pingdynasty.hatatap.HatatapTask" classpathref="lib_path"/>
<!-- Clean logs and output -->
<target name="clean">
<delete dir="${log_dir}" />
<delete dir="${test_output}" />
</target>
<!-- Recreate required folders -->
<target name="prepare">
<mkdir dir="${log_dir}" />
<mkdir dir="${test_output}" />
</target>

<!-- TEST TARGETS -->
<target name="alltests" depends="clean, prepare">
<antcall target="StartMLRecord"/>
</target>
<hatatap in="input/test_server.xml" out="output/output_test_server.xml">
<param name="server" value="${server}"/>
<param name="outputdir" value="${test_output}"/>
<classpath refid="lib_path"/>
</hatatap>
<antcall target="StopMLRecord"/>
</target>

<!-- LOG TARGETS -->
<!-- Begin recording events to the Master Log file, placed in the top level of the Sessiontests folder. -->

<target name="StartMLRecord" depends="oldMLDelete">
<record name="${log_dir}\ML_Log.txt" action="start" loglevel="info"/>
<echo file= "${log_dir}\ML_Log.txt" message="Master Log has started recording"/>
</target>
<!-- Halt recording to test Log file. -->
<target name="StopMLRecord">
<record name="${log_dir}\ML_Log.txt" action="stop"/>
</target>
<target name="oldMLDelete" description="Removes old Master log files">
<delete file="${log_dir}\ML_Log.txt"/>
</target>

</project>


In the above build script we have generic build features such as clean (Cleans up the output and log folders), prepare (prepares the required directories for the test run). We have also implemented a basic logging framework to trace progress of the test run and report test results. The main thing to note here is the hatatap task definition and the call. We make use of “com.pingdynasty.hatatap.HatatapTask” to execute the hatatap scripts.

Let’s go over the hatatap ant call.

<hatatap in="input/test_server.xml" out="output/output_test_server.xml">
<param name="server" value="${server}"/>
<param name="outputdir" value="${test_output}"/>
<classpath refid="lib_path"/>
</hatatap>

• The hatatap definition takes in an ‘in’ and ‘out’ attribute which is used to define the hatatap input script. Output file is the one generated after the test run.
• Then, we have ‘param’ tags which are used to pass any parameters to the hatatap script. In a real life scenario. We might be reading several parameters’ from properties and config files and make them available to the hatatap script by means of ‘param’ elements.
• We also have the classpath referenced here since we need to override libraries that are inbuilt.


6. Let’s create the hatatap scripts now. Create the below given XML (‘test_server.xml’) under the ‘input’ folder.

<?xml version="1.0" encoding="UTF-8"?>
<script xmlns="urn:hatatap" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:hatatap ../lib/script.xsd">

<param name="server" default="http://www.pingdynasty.com"/>
<param name="outputdir" default=""/>

<!-- Check if the Server is up and running -->
<send url="{$server}"></send>

<verify response-code="200"/>

<save href="{$outputdir}/test_server_response.xml"/>

</script>


So, above we have a very basic hatatap script, which tests the server response code.
• The script element is the root node, and declares the required namespaces for hatatap
• Then we have param elements required for the test run. Eventually, they will be substituted with values coming in from the Ant script. They also could prove handy for a failovers.
• Then follows the most important thing. The ‘send’ element which has a ‘url’ attribute, which denotes the server to be tested. There are other supported attributes like ‘method’ etc. You could find more information on the web site. When executed the ‘send’ makes a connection to the given ‘url’ and reads in the response. In a real life scenario we would also send along parameters’ along with the ‘send’. We will look at this further down.
• Once we have made a request and have a response, we could make use of built in elements to test them. ‘verify’ does just that. Above we are just testing the response code to be ‘200’ (OK) assuming the website is up and running.
• Last but not the least, we save the complete response to a file under the ‘output’ directory. Again in practical sense, this would be inspected or tested against.

7. So, we are set to do the first test run here.
• Open up your command prompt and navigate to the hatatap directory
• Run ‘ant’. This will run the default task and must give you the below results:

Buildfile: build.xml

clean:
[delete] Deleting directory C:\hatatap\log
[delete] Deleting directory C:\hatatap\output

prepare:
[mkdir] Created dir: C:\hatatap\log
[mkdir] Created dir: C:\hatatap\output

alltests:

oldMLDelete:

StartMLRecord:
[hatatap] input: C:\hatatap\input\test_server.xml
[hatatap] output: C:\hatatap\output\output_test_server.xml
[hatatap] info: send url: http://www.pingdynasty.com
[hatatap] info: response: 200 OK 579ms
[hatatap] info: save file: output/test_server_response.xml
[hatatap] finished test: test_server.xml

StopMLRecord:

BUILD SUCCESSFUL
Total time: 3 seconds


Before moving ahead, run through the ‘hatatap’ folder and look at the new files/folders the scripts generated for you.
• The ‘Log’ folder which has a log file with contents identical to what you saw on the console.
• The ‘output’ folder which contains the test run details and the response you saved.

Now what? Let’s look at adding some more cool and easy to use features to your hatatap script. In the above description, I mentioned that we could use XPATH with hatatap to test parts of the response.

Update your hatatap script as given below (text in bold):


<?xml version="1.0" encoding="UTF-8"?>
<script xmlns="urn:hatatap" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:hatatap lib/script.xsd">

<param name="server" default="http://www.pingdynasty.com"/>
<param name="outputdir" default=""/>

<!-- Check if the Server is up and running -->
<send url="{$server}"></send>

<verify response-code="200"/>

<save href="{$outputdir}/test_server_response.xml"/>

<verify test="count($dom//html/head) = '1'" msg="Expected '1' but was {count($dom//html/head)}" />

<verify test="count($dom//html/body) = '1'" msg="Expected '1' but was {count($dom//html/body)}" />

<verify test="$dom//title = 'Pingdynasty Technology Culture Art Politics'" msg="Expected 'Pingdynasty Technology Culture Art Politics' but was {$dom//html/title}" />


</script>


Above, we have added a few statements highlighted in ‘bold’ text. Before going through the above added statements, I would like to tell you a bit about the DOM support that comes along with hatatap. Hatatap gives you a default ‘dom’ object to play with the response. You could use this with XPATH to inspect parts of the response. What we just did above is that we sent a request to the server and read the response. We did inspect the response code. What about other parts of the response? Don’t you think we would need to inspect the response in more detail.? The added statements above do just that:

1. The first of the ‘verify’ statements uses the inbuilt ‘dom’ object to verify if we have a valid response which has a ‘head’ (html – head element) node. The ‘test’ attribute lets you implement conditional clauses and thereby make your test case more meaningful. Try updating the value expected to be ‘2’ and run the test. You would see the assertion error which is thrown when the test fails.

2. The second ‘verify’ statement is similar to the first one, but checks for an html ‘body’ node.

3. The third of the ‘verify’ statement makes a comparison of the expected title in the response.


Exploring ‘o:xml’ and integration with Hatatap:

We could make use of the power of ‘o:xml’ with hatatap test scripts to test complex scenarios and cases. We would try to explore a bit using the above example. Imagine a scenario wherein you would like to make a request to an application server, get the authorized session_id, and then make use of the session_id to test other modules of the application. How would you do that?

Here is how we would do it:
• Using hatatap connect to the app server and make a request
• Using Hatatap validate and save the Response in a file
• Using o:xml check for the file existence
• Using o:xml parse the XML
• Using o:xml set up local variables for use in the desired module.

Let’s update the Ant script to add another hatatap script, which would parse and read in data from an XML. For simplicity, let’s parse a simple XML file since the motive is to demonstrate parsing:

Add the following XML file under ‘input’.

File Name: sample.xml

<?xml version="1.0" encoding="UTF-8"?>
<sample>
<server>http://www.pingdynasty.com</server>
</sample>


Also add the hatatap script under ‘input

File Name: test_file_parser.xml

<?xml version="1.0" encoding="UTF-8"?>
<script xmlns="urn:hatatap" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:o="http://www.o-xml.org/lang/" xmlns:io="http://www.o-xml.org/lib/io/" xsi:schemaLocation="urn:hatatap ../lib/script.xsd">

<o:import href="lib/io/File.oml"/>

<param name="server" default="http://www.pingdynasty.com"/>
<param name="outputdir" default=""/>
<param name="inputdir" default=""/>

<o:if test="io:File($inputdir,'sample.xml').exists()">
<!-- logs -- >
<log msg="File exists. lets parse and fetch the Server URL ..."/>

<!-- Parse XML -- >
<o:set sample="io:File($inputdir,'sample.xml').parse()"/>

<!-- Set up a local variable by data being read from XML -->
<o:set url="$sample//server" />

<!-- output value -->
<log msg="Server URL from sample.xml = {$url}"/>
</o:if>

</script>


Things to note above are
• The namespace elements for ‘o:xml’ and the File API
• The import statement which imports the required library
• o:xml conditional blocks (if)
• XML parsing (Instead of a default ‘dom’ object we used in the previous example, here we have our own object by the name ‘sample’ defined, which is nothing but a DOM object.
• Setting up a local variable with data read from XML.


Next, we need to update the ‘build.xml’ so that we could run the test.

build.xml (update only the target – alltests)


<target name="alltests" depends="clean, prepare">
<antcall target="StartMLRecord"/>
<hatatap in="input/test_server.xml" out="output/output_test_server.xml">
<param name="server" value="${server}"/>
<param name="outputdir" value="${test_output}"/>
<classpath refid="lib_path"/>
</hatatap>
<hatatap in="input/test_file_parser.xml" out="output/output_test_file_parser.xml">
<param name="server" value="${server}"/>
<param name="outputdir" value="${test_output}"/>
<param name="inputdir" value="${test_input}"/>
<classpath refid="lib_path"/>
</hatatap>
<antcall target="StopMLRecord"/>
</target>

Running the above updated example should give you:

Buildfile: build.xml

clean:
[delete] Deleting directory C:\hatatap\log
[delete] Deleting directory C:\hatatap\output

prepare:
[mkdir] Created dir: C:\hatatap\log
[mkdir] Created dir: C:\hatatap\output

alltests:

oldMLDelete:

StartMLRecord:
[hatatap] input: C:\hatatap\input\test_server.xml
[hatatap] output: C:\hatatap\output\output_test_server.xml
[hatatap] info: send url: http://www.pingdynasty.com
[hatatap] info: response: 200 OK 516ms
[hatatap] info: save file: output/test_server_response.xml
[hatatap] finished test: test_server.xml
[hatatap] input: C:\hatatap\input\test_file_parser.xml
[hatatap] output: C:\hatatap\output\output_test_file_parser.xml
[hatatap] info: File exists. lets parse and fetch the Server URL ...
[hatatap] info: Server URL from sample.xml = http://www.pingdynasty.com
[hatatap] finished test: test_file_parser.xml

StopMLRecord:

BUILD SUCCESSFUL


Conclusion:

There are some cool features one could use, for example: hatatap allows you to run scripts generated by JMeter. So an automated test environment could make use of JMeter to also create ready to use hatatap scripts. Another thing to look at would be features available through o:xml. There is lot of stuff one could do, like defining functions, using loops, conditional blocks etc. Hatatap on its own has got great features and one needs to play around to get the best out of it and build a dynamic test environment. Hope this article gives you an environment to do just that.


References:

1. www.o-xml.org
2. http://hatatap.pingdynasty.com



Cheers,
Kris.