Introducing Jasmine to Java developers

To be honest, I was taking a pause from technical articles in order to work on my next tune. However while working on my pet project I decided to try out that Jasmine framework everybody was talking about.

And boy, do I like it!

Jasmine offers a neat, relatively concise solution for testing your JavaScript code’s behavior. And we all know how JavaScript code can misbehave from time to time. Even more than with Java code, I believe that testing JavaScript code can seriously cut down the time spent on fixing those bugs.

A whole new world

I used to rely on QUnit for defining the tests and Sinon.JS to help me out with mocks, spies, fakes, stubs, coffee, yes thank you very much.

Although I would highly recommend QUnit one might feel that it lacks that behavior-driven je-ne-sais-quoi some of us are looking for. That said, the framework is a excellent choice to start testing JavaScript code. JUnit users won’t feel disoriented.

I admit I had more problems when dealing with Sinon.JS. Not that it’s a bad framework, far from it, but I couldn’t help feeling overwhelmed by the amount of features Sinon offers. Anything you might need, there’s a good chance that Sinon.JS already thought of it. Of course that makes it a complex beast, and maybe not the best choice when you are trying to learn how JavaScript testing should be done.

What a Java developer like me wants in this case is a gentle introduction to JavaScript testing: a framework that is easy to learn and use, allowing me to focus on learning the best practices.

What Jasmine offers is exactly that:

  • A concise ‘core‘ to describe test suites and express expectations using a sensible amount of matchers. This would correspond to a Java developer’s JUnit.
  • A set of functions to spy on your methods. This corresponds more or less to the EasyMocks and Mockitos of the Java world.
  • Other useful tidbits, such as a clock() method to help you test setTimeout() or setInterval() callbacks.

Okay, so let’s get started then!

Installing and setting up Jasmine

Downloading and installing Jasmine depends on your environment. Node.js users can use npm, Python developers can use pip… These cases are all described in Jasmine’s excellent documentation pages, directly accessible from their GitHub website.

We’ll keep it simple and grab the standalone distribution here. It is then just a matter of unpacking and copying what’s needed in your project. In my case, I end up with with a structure like this:

The SpecRunner.html file is where it happens. The document includes all the necessary scripts to make the tests works:

  • Jasmine’s components
  • The JavaScript files to be tested
  • The JavaScript tests
  • The scripts’ dependencies, such as jQuery

Each component is included using the standard <script> tag. However nothing prevents you from setting up the dependencies with, say, Require.js for example.

Let’s move on and test some stuff!

Hello, test!

We want to test a JavaScript function that returns “hello, test!“.

In pure TDD / BDD style, let’s start by writing the test and save it under “src/test/webapp/app/js/hello-test.js“.

In there we describe what is being tested using the describe() function. Its second parameter is a function, meaning that we can write about any JavaScript code we want in there.

We then describe the desired behavior using the it() function, which also takes a function as its second parameter.

After we invoked function to test, we assert its returned value using the expect() method, followed by a matcher such as toEqual(), toBe(), etc. Hey, this is even better than JUnit asserts in terms of readability!

And by the way, did you notice how the whole test program reads? The code is close to being self-explanatory. If you keep your code clean, the test will remain incredibly readable and evident, which I believe is an essential aspect of good tests.

We edit the SpecRunner.html file (found in Jasmine’s distribution) to something like this:

We now run the test by opening SpecRunner.html in a browser. We should get something like:

jasmine-failed-test

Let’s move on and define that “hello” object then!

In “src/main/webapp/app/js/hello.js” we write the following:

We save the file, then refresh the SpecRunner page in the browser. The page should look like this:

jasmine-success-test

Congratulations, you just wrote the first of a long (I hope!) series of Jasmine tests!

I, spy

In Java, one uses something like EasyMock, jMock or Mockito to mock a class’ dependencies.

Now, I told you that Jasmine spies look like mock objects. This is not exactly true. Although they seem to behave in a similar way in the Jasmine tests you write, they are not the same thing: spies are based on an existing object, while mocks don’t.
Thinking of it, you mock an interface, not an implementation…
Okay, right, nowadays you can indeed create a mock from a class, but if you’re old like me you will remember a time where only interfaces could be mocked. Mocks don’t need the implementation, because they don’t rely on it. Well spies do! That said, when working with spies in Jasmine I hardly felt the difference.

So Jasmine comes with its own solution for spying on functions, and we’re going to use that to our advantage. This is an altered, simplified version of a small utility script I’m using for one of my projects:

The component’s objective is to display and eventually log notifications to the user. For example, invoking error() logs the message and shows an error box (simplified here to a mundane alert).

We could test alerts.restError() using Jasmine like we did here above, but then it would invoke alerts.error(), which would call alerts.show.error() which would display an alert box on-screen. I don’t know about you, but I’d rather unit-test each public method separately instead of testing a cascade of behaviors. Now, having error() log a message to the console is no big deal, but I’d like to avoid calling alerts.show.error() as I don’t want to spend my time closing alert boxes every time I run my tests!

So we are going to spy on alerts.error() in order to test alerts.restError(). Why? Because it will allow us to:

  1. Verify if the error() function is called by restError() as expected.
  2. Define what to do when the spied method is called: delegate to its real implementation, return a specific value, call a fake instead…

The first statement in the test sets up Jasmine to spy on the “error” function.

We then invoke the restError() function with some test data. When executing the function, Jasmine intercepts the call to the error() function, but does not run it. Should you need to actually invoke the spied function during the test, you can write something like:

But that’s not what we need here.

Finally, we use the expect() function to verify how that error() function has been called. We don’t want to actually call the function! We could write a different test that focuses on that function. But here we want to focus on restError()‘s logic and merely want to verify that error() has been called with the right message.

Don’t forget to load the files by adding them to your SpecRunner!

What about jQuery?

What happens when the function you need to test uses jQuery to work on the DOM? It this case, the best option would be to run the function on an HTML file that’s been specifically written for testing purposes. We can do that with the help of the jasmine-jquery extensions, which not only add a set of additional custom matchers to play with, but also provides an API to handle fixtures (that is, your HTML file for testing purposes).

Let’s take this function for example:

In this example we use jsForm, a very useful jQuery plugin which provides data-binding and form validation almost out-of-the-box. When jsForm detects a validation error, it marks the problematic input fields with the “invalid” class. Our isValid() function basically looks for that class and returns whether it found it or not.

So what we would like is to test the function on this HTML file:

The code to test this could look like this:

First we use Jasmine’s beforeEach() function, the equivalent to methods annotated with  JUnit’s @Before, to setup the suite’s specifications. We tell jasmine-jquery where to look for the fixtures (I put the HTML file in the same folder than the test) before we load the file we’re need for the test.

In the spec itself we initialize jsForm on our aptly-named ‘myInvalidForm‘. The “prefix” property allows jsForm to recognize the fields it must take care of. This is why in the HTML file the name of the input field in the form starts with a “myInvalidForm” prefix.

It is then just a matter of invoking Jasmine’s expect() function with the result of our isValid() function as its parameter, and use the “toBe()” matcher to ensure that result is false. And voilà!

Easy to use, easy to learn

Look back at the tests we wrote above: they are compact, readable, fluent.

This is something that is not that easy to achieve in Java, which is why some opt on using Groovy’s clutter-free syntax to express their tests. With Jasmine, this is achieved in seconds.

To that one can add the ease-of-use of the framework (the whole documentation is just one page. A large one, but not that large!) and the facility with which one can learn its various features. A Java developer can become productive with Jasmine in a matter of hours. It took me a short afternoon to go from being a Jasmine noob up to writing tests for five or six medium-sized JavaScript components for my project!

This is good because the time you don’t spend fighting with a testing framework is time you can use to improve the quality of the tests themselves.

In conclusion: if you haven’t done it yet give Jasmine a try: it’s mature, it does what is expected and it does it well.

Cheers!

 

One comment

Comments are closed.