Tuesday, June 19, 2007

How to compare XML in unit tests

When writing unit tests that compare basic XML I have historically just kept it simple and used string comparison. Granted it's highly error prone and an extra whitespace, tab, or incorrect order can cause the test to fail, but it's quick and when your test is simple enough it's sufficient. The strategy I tend to take when writing code also extends to my unit tests: start simple and improve the code as you go along. So when string comparison works; use it. When your unit test demands more then perhaps you should look at XMLUnit. It's actually quite simple and helps you avoid that nasty feeling you get when you use .equals().

Your first iteration might look something like this. Here you are expecting the method getResult() to return back '<basic/>'.

String expectedXml = "<basic/>";
String result = xmlBuilder.getResult();

assertEquals(expectedXml, result);

Now lets say getResult() returns back '<basic></basic>'. Now the test will fail even though the XML is equivalent. At first it's tempting to just add another test that expects the new format, and to be honest I seem to recall doing that. But now I have a new tool to use, so here is how you can use XMLUnit to test XML.

First start by adding it as a dependency. If you are a maven2 user add the following to your POM:


<dependencies>
<dependency>
<groupid>xmlunit</groupid>
<artifactid>xmlunit</artifactid>
<version>1.0</version>
<scope>test</scope>
</dependency>
</dependencies>

Next refactor your unit test to extend org.custommonkey.xmlunit.XMLTestCase instead of junit.framework.TestCase.
Finally replace the assertEquals() with assertXMLEqual() and that is it. So now your test would look like:

String expectedXml = "<basic/>";
String result = xmlBuilder.getResult();

assertXMLEqual(expectedXml, result);

And would pass for all combinations of '<basic/>'; such as '<basic></basic>'. As an additional hint you also might want to turn on ignoring whitespace. To accomplish this insert the following line in your code (I placed it in my constructor) to ignore whitespaces:

XMLUnit.setIgnoreWhitespace(true);

Now the other combinations will pass such as: '<basic>   </basic>'.

For more information on XMLUnit see their Java User's Guide.

Monday, June 18, 2007

Mac Safari for Windows

In case you were not aware, Apple released their browser, Safari, for windows last week. They reported there was over 1 million downloads in the first 48 hours.
You might consider giving it a try. They report that Safari is 1.6 times faster than Firefox in HTML performance, javascript performance, and application launch. With some basic browsing over the weekend at home I noticed a pretty big difference.

Here are some of the features I have come to love in Firefox that are missing in Safari; hopefully they will be added soon.

  1. They have a built in google search box but it doesn't provide suggestions (which I use all the time).
  2. Safari doesn't support the ability to close a tab with my mouse wheel click (I hate right clicking).
  3. No CRTL + ENT or CRTL + ALT + ENT to automatically complete keywords like wikipedia to http://www.wikipedia.org.
  4. When you click in the url, the url doesn't highlight. It just places the cursor where your mouse was instead of highlighting the whole url.
  5. Most importantly its missing the highly valuable restore session feature.
I couldn't consider seriously using safari, no matter how fast it is, until it supported a majority of these features that have made me an addict of firefox.

Proper way to access file resources in junit tests

As I was refactoring some JUnit tests recently I was reminded of an important fact on the proper way to read in a file.
In maven any file under /src/test/resources is automatically copied over to /target/test-classes. So for example lets say I need to read in a wsdl in my test. So I place the wsdl in the resources folder: /src/test/resources/test.wsdl. Now at first it might be tempting to reference the file with the following code:

File testWsdl = new File("/src/test/resources/test.wsdl");

and this will actually work on a windows machine; but what about linux, cygwin, or even solaris. Your next improvement might be to replace the '/' with File.separator:


File testWsdl = new File(File.separator + "src" + File.separator + "test"
    + File.separator + "resources" + File.separator + "test.wsdl");

This still isn't the best solution which is presented below:

URL url = this.getClass().getResource("/test.wsdl");
File testWsdl = new File(url.getFile());


Using the class's resource will locate the file in the test's classpath, which happens to be /target/test-classes. And this final solution will work on windows, linux, cygwin, and solaris.

Friday, June 8, 2007

Using Grizzly to create a simple HTTP listener

Today I would like to show how you can use Grizzly, a NIO framework for building scalable applications, to create a simple listener to service HTTP requests/responses. My main goal is to provide a simple example, because currently there is a lack of documentation on how to use this great framework (see jean's blog); although they have just made the javadocs available online.

My team and I have been working recently on developing a JBI Binding Component for RSS. To service the incoming HTTP GETs we decided to use Grizzly. Thankfully you can accomplish this with minimal effort.

The following is a simple example to start a TCP listener and respond with some simple text.

import com.sun.grizzly.http.SelectorThread;
import com.sun.grizzly.tcp.Adapter;
import com.sun.grizzly.tcp.OutputBuffer;
import com.sun.grizzly.tcp.Request;
import com.sun.grizzly.tcp.Response;
import com.sun.grizzly.util.buf.ByteChunk;

import java.net.HttpURLConnection;

public class EmbeddedServer implements Adapter {

public static void main(String[] args) {
SelectorThread st = new SelectorThread();
st.setPort(8282);
st.setAdapter(new EmbeddedServer());
try {
st.initEndpoint();
st.startEndpoint();
} catch (Exception e) {
System.out.println("Exception in SelectorThread: " + e);
} finally {
if (st.isRunning()) {
st.stopEndpoint();
}
}
}

public void service(Request request, Response response)
throws Exception {
String requestURI = request.requestURI().toString();

System.out.println("New incoming request with URI: " + requestURI);
System.out.println("Request Method is: " + request.method());

if (request.method().toString().equalsIgnoreCase("GET")) {
response.setStatus(HttpURLConnection.HTTP_OK);
byte[] bytes = "Here is my response text".getBytes();

ByteChunk chunk = new ByteChunk();
response.setContentLength(bytes.length);
response.setContentType("text/plain");
chunk.append(bytes, 0, bytes.length);
OutputBuffer buffer = response.getOutputBuffer();
buffer.doWrite(chunk, response);
response.finish();
}
}

public void afterService(Request request, Response response)
throws Exception {
request.recycle();
response.recycle();
}

public void fireAdapterEvent(String string, Object object) {}
}

That's basically it in a nutshell. So far Grizzly has worked out well for us and is currently being used in Glassfish and Sailfin.

As a side note I have also been reading a lot about groovy recently, so as an added bonus and a personal challenge to write my first groovy script, here is the equivalent groovy code. Remember that groovy scripts can use existing java classes and can also be consumed by java classes.
import com.sun.grizzly.http.SelectorThread
import com.sun.grizzly.tcp.Adapter
import com.sun.grizzly.tcp.Request
import com.sun.grizzly.tcp.Response
import com.sun.grizzly.util.buf.ByteChunk
import java.net.HttpURLConnection

class BasicAdapter implements Adapter {
public void service(Request request, Response response) {
def requestURI = request.requestURI().toString()

println 'New incoming request with URI: ' + requestURI
println 'Request Method is: ' + request.method()

if (request.method().toString().equalsIgnoreCase("GET")) {
response.status = HttpURLConnection.HTTP_OK
def bytes = 'Here is my response text'.bytes

def chunk = new ByteChunk();
response.contentLength = bytes.length
response.contentType = 'text/plain'
chunk.append(bytes, 0, bytes.length)
def buffer = response.outputBuffer
buffer.doWrite(chunk, response)
response.finish()
}
}

public void afterService(Request request, Response response) {
request.recycle()
response.recycle()
}

public void fireAdapterEvent(string, object) {}
}

def st = new SelectorThread()
st.port = 8282
st.adapter = new BasicAdapter()
try {
st.initEndpoint()
st.startEndpoint()
} finally {
if (st.isRunning()) {
st.stopEndpoint()
}
}