Skip to main content

What to test when testing Javascript

Part two in the series Test Driven Development with Javascript.



By now you've probably read and heard a lot warning you off testing through the UI and testing the user interface generally. Those still holds true for Javascript so you may be wondering if there is anything left to test. Time for some code!

Not very testable code

function validate() {
    if(document.simple_form.name.value == "" ||
        document.simple_form.email.value == "")
    {
        alert("Please fill out all fields before clicking submit!");
        return false;
    }
}


In this there is some logic that is not directly tied to the UI. So there is something we can test. But it’ll need a bit of a rework to make it testable

function isFormValid(formElement) {
    return (formElement.name.value != "" && formElement.email.value != "");
}

function validate() {
    if (!isFormValid(document.simple_form)) {
        alert("Please fill out all fields before clicking submit!");
        return false;
    }
}


Now we have a testable function isFormValid. It’s an example of separating the logic from the UI and the DOM interaction. There is still a DOM element being used but that’s fine as it is easy to create or perhaps mock in writing the test.

Code not worth testing

$(‘.button).click(function() {
  $(‘#my_quote’).css(‘background-color’, ‘red’);
}


This code is still testable despite using some jquery magic. It’s easy to mock out jquery or fake events and DOM enough to test it. Do not test this code. There is no real logic in there and it’s heavily tied to the UI interaction. This may mean a lot of the Javascript you’re writing does not have tests and that’s okay. Interactions like this are still best tested manually and if they fail in a harmful way will be obvious to anyone using the system. Logic errors however can be much harder to spot through manual testing.

Testable closures

function passwordCheckClosure(password) {
  return function(comparePassword) {
    return comparePassword === password;
  }
}


I love closures and closures like this are still really easy to test. Just as I would still expect you to create private methods and properties when doing TDD in C++ or Java. In Javascript I would still expect you to write closures to help build your code into clear abstract interfaces. But as with private methods and constructors you need to avoid hiding everything in closures.

Untestable closures

function validate() {
    function isFormValid(formElement) {
        return (formElement.name.value != "" &&
            formElement.email.value != "");
    }

    if (!isFormValid(document.simple_form)) {
        alert("Please fill out all fields before clicking submit!");
        return false;
    }
}


This is just a slight variation on our form valid check above. But now we cannot access isFormValid to test it separate to the validate method as a whole.

 Dependency injection with a closure

// formElenment - jquery reference to the form object
// alertMethod - alert on the browser. something else for console or tests
function attachFormValidator(formElement, alertMethod) {
    function isFormValid() {
        return (formElement.find(‘#name’).val() != "" &&
            formElement.find(‘#email’).val() != "");
    }
    formElement.submit(function(e) {
        if (!isFormValid()) {
          alert("Please fill out all fields before clicking submit!");
          e.preventDefault();
        }
      }
    );
}


This may seem harder to test. We've introduced expections that we’re using jquery and put everything within a function that does not return anything. But it’s actually very testable since we can easily mock out all the objects that we send in and trigger and test those actions through those mocks. Because objects are cheap and easy to construct that means it’s cheap and easy to mock things out.

Next time
Running tests with Jasmine and your web browser

This is part of a series I'm writing for Agile+ on Google+ so you can follow as I post them there

Popular posts from this blog

When Agile is not common sense

It's been about 8 years since I got my Agile training from James Grenning and started an Agile Pilot with my team. Right from the start I was super keen and got people practising TDD and Pair Programming even though I didn't really know if that effort would pay off. I just knew we had lots of problems with the old way so I wanted to embrace the new.

So I'd run my own little sessions with the broader team in Sydney sharing Agile concepts and ideas. Giving us a chance to discuss things and keep learning long after James had left. But often I would present new ideas and a few people in the group would respond "oh of course, that's just common sense". Initially I thought it was great as they were embracing these ideas as valuable even though it was not what we were doing before. But after a while I started to get frustrated because they we're not really listening. If some small part of a concept fit nicely with their "common sense" then they took th…

Setting up Fitnesse on Ubuntu in 7 steps

Some pretty basic steps but just to make sure it's here for everyone to see. Setting up fitnesse and running the jar is easy enough. Just go to http://fitnesse.org/ and get started and do it on your desktop just to see it in action. But for me that wasn't good enough I wanted it to run as service on ubuntu.

I stole a few tricks from how ubuntu runs jenkins and setup fitnesse a similar way.

1. Create a user and group for fitnesse (optional)
I didn't do this because I wanted tomcat, jenkins and fitnesse all running as the same user. Call it laziness to avoid any permissions classing but it doesn't change the process that you need to create or choose what user you're going to make it run as. Don't make it run as your user or root!

2. Download the jar file and place it in /usr/share/fitnesse
Make the folder too of course. It can belong to root as long as the fitnesse user has read access

3. Create the folder to run in at /var/lib/fitnesse
Fitnesse user needs write…

RestFixture

So most of the tests I'm writing now in Fitnesse are using RestFixture. Being able to do all this black box style testing has helped me get a lot of tests up and running without having to change the existing code base. Now I've taken a step future with my own little fork so I can use scenarios and build nice BDD style scripts. But first I want to give me own quick guide to using RestFixture

Step 1: Installing
You can dive straight in by grabbing the latest jar files for RestFixture here https://github.com/smartrics/RestFixture/downloads
If you know what you're doing can get the nodep version to work nicely along side other libraries you may be including in Fitnesse. But I grabbed the 'full' version and unzipped it into a RestFixture folder alongside my FitNesseRoot folder.
Step 2: Write your first test
I took advantage of the built in Fitnesse api as a basic test and wrote a page called RestFixture with the following contents
!define TEST_SYSTEM {slim} !path RestFix…