Thursday, November 11, 2010

TDD with javascript, JQuery, QUnit and Maven

I recently attended a Test Driven Development (TDD) training by Brett Schuchert of ObjectMentor.com and find it really useful! With TDD your code maintenance and evolution are easier and regression testing is less likely to have bugs because it prevents bugs from happening in the first place.

But what is TDD exactly? The instructor defined it as a "design practice". It uses tests as mechanism for discovery and feedback. He also added that TDD is not always the right thing to do. It depends on your application and requirements.

I was surprised to know that TDD idea has been around since late 50's. The original Mercury Rocket Project uses TDD.

The training inspired me and had me started looking on the how to apply TDD in javascript. The instructor advised some framework such as jsunit, which is good, but since our FEDs (Frontend developers) uses JQuery, I have to find a framework that has less learning curve and easy to use.

I searched and found out about Qunit (http://docs.jquery.com/Qunit). QUnit is a powerful, easy-to-use, JavaScript test suite. It's used by the jQuery project to test its code and plugins but is capable of testing any generic JavaScript code (and even capable of testing JavaScript code on the server-side).

Qunit is cool! But I have to integrate it with our maven project. I want our "maven build" to break if javascript unit test fails. (like how JUnit works ).

After further research, I found Rhino. Rhino is an open-source implementation of JavaScript written entirely in Java. It is typically embedded into Java applications to provide scripting to end users.

Given these 2 technologies, I configured maven to use qunit and Rhino to do javascript unit testing, and here's how I did it. ( If you have a better solution or comments, please let me know. )

One thing to keep in mind, when using this combo is that, you have to separate your javascript calculation logic from DOM-manipulation logic. You can extract them to a function or to an object, and unit test them separately. Use selenium to test the DOM-manipulation part. The reason being is that, you are executing javascript without the browser, and DOM-manipulation logic is easier to test using Selenium.

Here's an example on how to do it.

1) adder.js - This file is where you put the functions that you want to test.

//function that adds.
function adder(x,y) {
    return x + y;
}

2) adderTest.js - This file is your unitTest.
test("Adding numbers works", function() {
        expect(2);
        ok(adder, "function exists");
        equals(4, adder(2, 2), "2 + 2 = 4");
            }
);

3) suite.js - This file is your suite. It can contain many test.js files.

load("src/main/webapp/WEB-INF/js/qunit/qunit.js");

QUnit.init();
QUnit.config.blocking = false;
QUnit.config.autorun = true;
QUnit.config.updateRate = 0;
QUnit.log = function(result, message) {
    if(result == false) {
        print("FAILED: " + message);
        java.lang.System.exit(0);
    }else {
       print("PASS: " + message) ;
   }

};

load("src/main/webapp/WEB-INF/js/adder.js");
load("src/main/webapp/WEB-INF/js/adderTest.js");


NOTE:
a) Make sure you have downloaded qunit, and put it in your js directory. It is being referenced by suite.js
b) since I am using Maven 2, the path should starts from your base directory. (In my setup, it's in src/main/webapp/WEB-INF/js)

4) Add a dependency on Rhino and a maven plugin in your pom.xml:

...
<dependency>
   <groupid>rhino</groupid>
    <artifactid>js</artifactid>
    <version>1.7R1</version>
</dependency></pre>

...

<plugin>
    <groupid>org.codehaus.mojo</groupid>
    <artifactid>exec-maven-plugin</artifactid>
    <version>1.1</version>
    <executions>
        <execution>
        <phase>test</phase>
        <goals>
            <goal>java</goal>
        </goals>
        </execution>
    </executions>
    <configuration>
        <mainclass>org.mozilla.javascript.tools.shell.Main</mainclass>
        <arguments>
            <argument>-opt</argument>
            <argument>-1</argument>
            <argument>${basedir}/src/main/webapp/WEB-INF/js/suite.js</argument>
        </arguments>
    </configuration>
</plugin>
 



Now, when you execute "mvn test", it will execute the test suite, and run your test cases. Try adding this line in your addTest.js, and make sure to update the expect() method call.

equals(10,newAddition(5,0), "5 + 0 = 5");


making it:
test("Adding numbers works", function() {
     expect(3); //  <-- change to 3.
     ok(adder, "function exists");
        equals(4, adder(2, 2), "2 + 2 = 4");
        equals(10,adder(5,0), "5 + 0 = 5");
            }
);
);
It will break your maven build and tell you:

PASS: function exists
PASS: <span class="test-message">2 + 2 = 4</span>
FAILED: <span class="test-message">5 + 0 = 5</span>

Thursday, October 07, 2010

Pragmatic unit testing with JUnit

Here's a great slides about JUnit, Hamcrest, Parameterized Testing, JUnit Rules (Verifier, Timeout, etc), and also about naming classes.

Friday, September 10, 2010

Friday, August 27, 2010

implementing fastinfoset using CXF

I recently experimented with FastInfoset (FI) and I got it working using CXF. FastInfoset is a standard that specifies a binary encoding format for XML Information set. It aims to provide more efficient serialization than text-base XML format.

One can think of FI as gzip for XML, though FI aims to optimize both document size and processing performance, whereas gzip optimizes only the size. While the original formatting is lost, no information is lost in the conversion from XML to FI and back to XML.

The java implementation of FI is available as part of GlassFish project. ( https://fi.dev.java.net/ ).


1) In your pom.xml make sure you get the cxf version 2.2.7 or later (Thanks to Daniel and Igor for fixing the bug promptly) and add FI dependency:

<dependency>
<groupId>sun-fi</groupId>
<artifactId>FastInfoset</artifactId>
<version>1.2.2</version>
</dependency>

....

<repository>
<id>fastinfoset</id>
<url>http://repository.jboss.org/maven2</url>
</repository>


2) in your service add this annotation.

@Produces({"application/xml","application/fastinfoset" })


3) in your configuration, add the interceptor, customTypes for the JAXBElementProvider and define it in your service:


<bean id="fastInfosetOutInterceptor" class="org.apache.cxf.interceptor.FIStaxOutInterceptor" />
<bean id="fastInfosetInInterceptor" class="org.apache.cxf.interceptor.FIStaxInInterceptor" />

<util:list id="customTypes">
<value>application/xml</value>
<value>application/fastinfoset</value>
</util:list>

<bean id="jaxbProvider" class="org.apache.cxf.jaxrs.provider.JAXBElementProvider">
<property name="produceMediaTypes" ref="customTypes" />
<property name="consumeMediaTypes" ref="customTypes" />
</bean>


<!-- JAX-RS endpoint configuration -->
<jaxrs:server id="sampleService" address="/">
<jaxrs:serviceBeans>
<ref bean="sampleServiceBean" />
</jaxrs:serviceBeans>
<jaxrs:providers>
<ref bean="jaxbProvider" />
</jaxrs:providers>
<jaxrs:outInterceptors>
<ref bean="fastInfosetOutInterceptor" />
</jaxrs:outInterceptors>
<jaxrs:inInterceptors>
<ref bean="fastInfosetInInterceptor" />
</jaxrs:inInterceptors>
</jaxrs:server>


4) When testing, make sure you add Accept header variable with value “Application/fastinfoset”, before hitting the endpoint. (Using the browser will not work).

TO TEST:
curl -H "Accept: Application/fastinfoset" http://localhost:8080/services/samples/getSample?id=9
SampleResponse{??version@1????Samples????ResponseMetadata???Service???Name?Foo????URL?http://foo????Hostname?

You can also add a FireFox Add-on called “Poster”