Hey, I'm writing a book too :) Click the link below to preorder and save ;)
»» Get the Only Reference You'll "ever" Need on JavaScript Engineering Interviews, Now! ««
September 16th in Unit Testing by .

Unit Testing with JavaScript — Part 1: Best Practices

You need testing - Yo!

You need testing - Yo!

Despite the fact that unit testing can add considerable value to the quality of your code, it can do more harm than good, on the long run, if you do not implement it correctly.

It all depends on the quality of the tests you design. And it, in turn, depends on how well you understand the basic principles of “unit testing“.

Navigation

This is Part 1 of a 4-part Series on JavaScript Unit Testing. Here are links to all of the parts:

What is a Unit Test

First things first. Let us start with a definition of a unit test:

In computer programming, unit testing is a method by which individual units of source code are tested to determine if they are fit for use.

A unit is the smallest testable part of an application. In procedural programming a unit may be an individual function or procedure.

The difference between a good and a bad unit test cannot be easily seen. Additionally, the methodology of learning to write good unit tests is far from obvious. Even being a rock-star developer with years of experience won’t magically make you create better-than-average unit tests unless you learn to change your habits.

Unit Testing is not Integration Testing

There is a totally different concept of “testing”, namely “integration testing“.

While unit tests focus on individual “unit“s of code, integration tests control how all the individual units work as a whole. Integration tests are not replacements for unit tests; they complement one another.

To see how integration test can be used to complement your unit tests, and to read up further on integration testing, you may want to check out projects like Selenium, or WatiN.

Any Unit Testing is Better than None

Refactoring without tests leads to bugs. Rather than avoiding unit tests, because they are badly written, working on to improve these test should be the approach to follow.

Writing unit tests puts you into a completely different state of mind:
Instead of writing code that “just works”:

  • You start thinking of writing code that’s not dependent to other modules,
  • You start thinking of fail-case scenarios that the code won’t run as expected,
  • You start thinking about code as a contract,
  • You start visualizing your unit as a black box with definite input/output relationships,
  • You start thinking in “should“s, instead of “could“s,
  • You constantly and unconsciously as yourself “how do I test this one line of code”.

But When You Have a Hammer…

… everything looks like a nail, doesn’t it ;) ?

This may surprise you but unit testing is not for finding bugs. If you’re trying to find bugs, you had better test the integrity of the application by running the whole application together and manually testing it for bugs.

There are paradigms for different cases:

  • If you want to find out things which don’t work as you want them to be (i.e. bugs), do manual testing,
  • If you want to detect things that used to work which do not work as expected anymore (i.e. regressions), do automated integration tests,
  • If you want to design robust and reliable software components with solid input/output relationships, do unit testing.

So, What Makes a Good Unit Test?

A good unit test suite is invaluable. It makes it easier to refactor and expand your code while keeping an eye on each individual component’s behavior.

I will go as far as to say that a suite of well-crafted unit tests is a means to document your code.

Here are the common characteristics of a good unit test suite:

  • Each test is orthogonal to one another:
    That is to say, each test should be independent from one another. changing the behavior of a test should not affect other tests. And changing the order of the tests in a suite, should not change the outcome of each individual test.
  • Do not make redundant assertions:
    Do not assert anything that is also covered by another test. Keep one and only one logical assertion for each test, because if you have more than one logical assertion per test, a failing test’s reason of failure can become ambiguous.
  • Try mock objects, whenever you can.
  • Have a clear and consistent naming scheme:
    Do not use generic names for your unit tests such as “tests page login”.

    I generally use the following pattern for naming my unit tests:

    “{some action} SHOULD {some outcome} {some condition}”,

    as in

    “Page Login should return false if email is invalid.”

Inversion of Control / Dependency Injection

Each unit test should contain knowledge about the behavior of a single unit of code that it’s testing.

If the unit’s behavior changes so should the corresponding unit test. Though the test should not contain any assumption at all about other parts of your code base.

You should be able to test your modules independently. Changes to one unit should not generate a chain reaction of failures in other units. Inversion of Control is an excellent design pattern adhering to this philosophy.

Conclusion

Unit tests are the design specs of the module’s behavior, they are not an observation of everything the code does. Unit tests query how the module does its job, rather than what it does.

In a nutshell, carefully thinking about your unit tests before coding will help you focus and see things you’ve never seen before. Your code will not look the same again ;)

… What’s Next?

This article was the “talking the talk” part of unit testing. In the upcoming article we will “walk the walk” and create a JavaScript unit testing suite from the ground up ;) .

Until then, feel free to share your comments and suggestions :) .
Do you favor unit tests?
What practices do you adhere when testing your code?
I’d love to hear them :)

It's me: Volkan! Jack of all Trades, Samurai of JavaScript ;) Since 2003, I’ve been doing front-end development on client-heavy AJAX web applications. Currently, I'm a JavaScript Hacker at "SocialWire". Before that I was a VP of Technology at "GROU.PS", a well-known do-it-yourself social networking platform that allows people to come together and form interactive communities around a shared interest or affiliation. Before that, I was a JavaScript Engineer at "LiveGO", a globally known social mash-up. Before that, I was the CTO of a business network ("cember.net") which was acquired by Xing AG for around 4.2M Euros. See my linkedin profile to find more about me; I also share worth-following bits an pieces on twitter.

VIsit Volkan Özçelik's website

8 Comments

Leave a Reply





o2.js _
Fork Ribbon