Skip to content

Instantly share code, notes, and snippets.

@bittersweetryan
Last active December 20, 2015 13:49
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bittersweetryan/6142060 to your computer and use it in GitHub Desktop.
Save bittersweetryan/6142060 to your computer and use it in GitHub Desktop.
Tips and Tricks for testing JavaScript

#JavaScript Unit Testing Tips and Tricks#

###Please add your your tips and tricks for unit testing JavaScript as comments to this gist. I'll pull them into the main text and credit your contribution.###


  • Create/use snippits to scaffold testing methods ( i.e. if you are using BDD scaffold describe and it methods)
  • Write tests with the same care as production code
    • DRY. Factor out repeated code into methods
    • Well formatted
    • Write with refactoring in mind, tests will change!
  • when running JS tests, typically they are run in one browser process. Meaning any changing to the global namespace could affect every test ran after the changes. ( via [@oobert] (https://github.com/oobert) )
  • Break apart large functions into smaller ones.
  • A single test file, or spec for each JS file. AKA Don't write one test for ALL your JavaScript!
  • Consider injection for global dependencies such as window, document, and/or jquery. This will allow those dependencies to be mocked/faked during tests while reducing the chance that the mocking/faking will affect other tests. Example:

The following method will be hard to test in an isolated way because any changes to "window" from other tests may affect this test and any test for this method may affect other tests.

function doSomething(){
    window.location.hash = 'myHash';
}

doSomething();

An easy example of using injection would be like this. Instead of calling the global window variable, have it passed into the method instead. It will allow the dev to create an object that looks a lot like "window" but will not affect other tests.

function doSomething(win){
   win.location.hash = 'myHash';
}

doSomething(window);

This is not a hard and fast rule but I have found this to be beneficial when testing things like Knockoutjs ViewModels. It allows the direct dependency on globals like window, document, jquery be remove and at the same time allows unit tests to mock/fake them. It does not mean you have to but the option is there if needed.
( via [@oobert] (https://github.com/oobert) )

  • use spies on the jQuery.fn namespace to test jQuery plugins instead of testing the output. E.G. if you have some code that calls $('#theForm').valid() spy on the $.fn call. Using Jasmine the spy would look like spyOn( jquery.fn, 'valid' ).
  • Favor function references as callacks over anonymous functions:
//instead of
$('...').on( 'click', function( e ){
   ...
});

//do this
var callback = function( e ){
   ...
};

$('...').on( 'click', callback);
  • With the BDD approach, write requirements first, then build unit tests around those requirements. This gives you have human-readable requirements to fall back on, along with a unit test suite that tests everything. ( via [@klamping] (https://github.com/klamping) )
  • Try and break your tests. This is especially important for adding unit tests to code that's already existing. Unit tests can have bugs too, so there may be times when your testing something and it's giving you a false positive on the test. Go back, remove the line of code that you think is passing the test, and make sure that test now fails. This way you can always be sure that the tests are valid. ( via [@klamping] (https://github.com/klamping) )
  • Make sure you have tests that fail. Make sure that weird input into a function or class fails gracefully. If your function expects two numbers to be passed in, but someone passes in a word, then that function should throw an error. You should have a test for this situation. ( via [@klamping] (https://github.com/klamping) )
  • if you have a function that your having a really hard time writing a test for, there's a good chance that you need to refactor the function. Your function is either doing too much or it's making too many assumptions (or both). Simplify until writing the test is really easy. It's a win in that you have a less fragile test suite and that you have a more module codebase.( via [@klamping] (https://github.com/klamping) )
@Oobert
Copy link

Oobert commented Aug 5, 2013

Remember that when running JS tests, typically they are run in one browser process. Meaning any changing to the global namespace could affect every test ran after the changes.

Consider injection for global dependencies such as window, document, and/or jquery. This will allow those dependencies to be mocked/faked during tests while reducing the chance that the mocking/faking will affect other tests.

Example:

The following method will be hard to test in an isolated way because any changes to "window" from other tests may affect this test and any test for this method may affect other tests.

function doSomething(){
   window.location.hash = 'myHash';
}

doSomething();

An easy example of using injection would be like this. Instead of calling the global window variable, have it passed into the method instead. It will allow the dev to create an object that looks a lot like "window" but will not affect other tests.

function doSomething(win){
   win.location.hash = 'myHash';
}


doSomething(window);

This is not a hard and fast rule but I have found this to be beneficial when testing things like Knockoutjs ViewModels. It allows the direct dependency on globals like window, document, jquery be remove and at the same time allows unit tests to mock/fake them. It does not mean you have to but the option is there if needed.

@klamping
Copy link

With the BDD approach, I've always found it easier to write my requirements for whatever code I'm writing, and then build my unit test structure around those requirements. The benefit of doing this over just writing the unit tests is then you have human-readable requirements to fall back on, along with a unit test suite that tests everything.

Another tip is to try and break your tests. This is especially important for adding unit tests to code that's already existing. Unit tests can have bugs too, so there may be times when your testing something and it's giving you a false positive on the test. Go back, remove the line of code that you think is passing the test, and make sure that test now fails. This way you can always be sure that the tests are valid.

Make sure you have tests that fail. Make sure that weird input into a function or class fails gracefully. If your function expects two numbers to be passed in, but someone passes in a word, then that function should throw an error. You should have a test for this situation.

My last thought is that if you have a function that your having a really hard time writing a test for, there's a good chance that you need to refactor the function. Your function is either doing too much or it's making too many assumptions (or both). Simplify until writing the test is really easy. It's a win in that you have a less fragile test suite and that you have a more module codebase.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment