##1.- Unit Testings
-
A Unit testing is a piece of cide that serves to check if another piece of code works properly. It is code thar serves to test another code.
-
These unit testings should:
- be able of launching them automatically (this is specially important for a continuous integration)
- test the maximum code possible (code coverage as high as possible)
- be able of be executed as many times as needed
- be independent. The execution os one test, shouldn't affect the execution of the other.
- maintain the quality of the code (code convention, good practices, documentation,...)
-
As long as the source code in a project grows, it gets more and more important being able to check in a simple way that the changes and the new code doesn't break anything of the existing code
-
In Javascript this is even more important, because having the tests we can check automatically that our code works well in different environments (IE, Firefox, Chrome,...)
-
IN TDD, the unit is the smallest piece of code that can be tested. Most of the times this unit has a correspondence with a function/method.
-
The test is usually also a method/function so a unit testing use to be a method (test) that test another method (unit)
-
Sometimes several unit testings are written for the same method/object having, for example, every one of them testing a specific behaviuour. These tests that test a group of related code (like an object w/ different methods) are usually grouped in a test case
-
Test Cases are usually also grouped in test suites
###Code coverage
-
The code coverage is a way of measure the proportion of aource code that is really being tested by a test suite
-
The code coverage is usually measured by a software that analizes the code and the tests. For PHP PHPUnit with XDebug is usually used. For JS, we can Sonar or Istambul with Karma
This is the code to test (the 'unit')
function add(a, b) {
return a + b;
}
And this is the code we're using to test it ( the 'testing')
// Test function
if (add(1, 2) !== 3) {
document.write('<p style="color: red;">add(1, 2) does not add up to 3</p>');
} else {
document.write('<p style="color: green;">add(1, 2) OK</p>');
}
if (add("2", 2) !== 4) {
var msg = "Numbers as strings don't add";
document.write('<p style="color: red;">' + msg + '</p>');
} else {
document.write('<p style="color: green;">add("2", 2) OK</p>');
}
We can execute this test from here
This example is very simple but illustrate several things about unit testings:
- The function is tested from several angles (we check what the function returns when passing different types of data to it)
- We compare the returned value with an expected value
- If these values match we show a green OK
- If these values doesn't match we show an error message to help us locate the problem
The previous example didn't pass all the tests, but if we improve the code
function add(a, b) {
return parseInt(a) + parseInt(b);
}
This new code pass the tests, so it follows the criteria especified in the test
##2.- TDD y BDD
###Test Driven Development (TDD)
-
TDD (Test Driven Development) is a methodology, way of programming, a workflow, that basically consist in doing the tests first (specifying what our code should do) and after that, do the code that pass these tests
-
The recommended workflow in TDD is:
- We write the tests for a new feature (assuming method names, input parameters, returned values...)
- We execute the tests and check they fail (we still haven't written the code to be tested)
- We write the most simple solution that pass the tests
- We refactor the code (cleaner and more efficient code that still pass the tests)
- We repeat these steps for another feature
-
By applying TDD we can focus in the interface (API, methods, inputs & outputs) more than in the details of the implementation
###Behavior Driven Development (BDD)
-
BDD (Behavior Driven Development) is a specialized version of TDD that is more focused on testing (specifications of) behaviours of software
-
It uses a more human language to write the tests and is not focused as much in how the API should work and more in testing the specific feature does what is expected to
##3.- Testing Frameworks
Looking for a better JavaScript unit test tool
Heroes of JavaScript and Testing
-
There are a few Frameworks that will ease to us the task of doing the tests of our code
-
These frameworks offer some assertions that we can use to write the tests
###Jasmine
Jasmine is a framework used to test Javascript code oriented to BDD (to test behaviours), but it can also be used for TDD (testing API).
A test suite in Jasmine is declared as a global function describe
that recevies:
- the name of the suite
- a function with code that implements the test
The specs are declared with the global function it
that receives:
- a description of the specification
- a function with one or more expectations
The expectations (expected behaviours) are described with the function expect
and a matcher (toBe
, toEqual
, ...):
describe("A suite", function() {
it("contains spec with an expectation", function() {
expect(true).toBe(true);
});
});
Some matchers that Jasmine offer by default (we can build our own) are:
-
expect(x).toEqual(y);
compares objects or primitivesx
andy
and passes if they are equivalent -
expect(x).toBe(y);
compares objects or primitivesx
andy
and passes if they are the same object -
expect(x).toMatch(pattern);
comparesx
to string or regular expressionpattern
and passes if they match -
expect(x).toBeDefined();
passes ifx
is notundefined
-
expect(x).toBeUndefined();
passes ifx
isundefined
-
expect(x).toBeNull();
passes ifx
isnull
-
expect(x).toBeTruthy();
passes ifx
evaluates totrue
-
expect(x).toBeFalsy();
passes ifx
evaluates tofalse
-
expect(x).toContain(y);
passes if array or stringx
containsy
-
expect(x).toBeLessThan(y);
passes ifx
is less thany
-
expect(x).toBeGreaterThan(y);
passes ifx
is greater thany
-
expect(function(){fn();}).toThrow(e);
passes if functionfn
throws exceptione
when executed
For this piece of code:
// your applications custom code
function addValues( a, b ) {
return a + b;
};
A Unit Testing in Jasmine could be:
// the Jasmine test code
describe("addValues(a, b) function", function() {
it("should equal 3", function(){
expect( addValues(1, 2) ).toBe( 3 );
});
it("should equal 3.75", function(){
expect( addValues(1.75, 2) ).toBe( 3.75 );
});
it("should NOT equal '3' as a String", function(){
expect( addValues(1, 2) ).not.toBe( "3" );
});
});
More info:
- Magnum CI: The Jenkins Chronicles #1 – Intro to JsTestDriver
- Testing Your JavaScript with Jasmine
- Unit test JavaScript applications with Jasmine
###QUnit
QUnit is a unit testing framework (for client side) that allow us to test our javascript code in a simple way.
It is the framework used to test the projects jQuery, jQuery UI, jQuery Mobile... and even itself (QUnit)
Some assertions:
ok( state, message )
A boolean assertion, equivalent to CommonJS's assert.ok() and JUnit's assertTrue(). Passes if the first argument is truthy.
// Let's test this function
function isEven(val) {
return val % 2 === 0;
}
test('isEven()', function() {
ok(isEven(0), 'Zero is an even number');
ok(isEven(2), 'So is two');
ok(isEven(-4), 'So is negative four');
ok(!isEven(1), 'One is not an even number');
ok(!isEven(-7), 'Neither is negative seven');
})
equal( actual, expected, message )
A non-strict comparison assertion, roughly equivalent to JUnit assertEquals.
test( "equal test", function() {
equal( 0, 0, "Zero; equal succeeds" );
equal( "", 0, "Empty, Zero; equal succeeds" );
equal( "", "", "Empty, Empty; equal succeeds" );
equal( 0, 0, "Zero, Zero; equal succeeds" );
equal( "three", 3, "Three, 3; equal fails" );
equal( null, false, "null, false; equal fails" );
});
strictEqual( actual, expected, message )
A strict type and value comparison assertion.
test( "strictEqual test", function() {
strictEqual( 1, 1, "1 and 1 are the same value and type" );
});
For this piece of code:
// your applications custom code
function addValues( a, b ) {
return a + b;
};
A Unit Testing in QUnit could be:
// the QUnit test code
test("test addValues(a, b) function", function() {
equal(addValues( 1, 2), 3, "1 + 2 = 3" );
equal(addValues( 1.75, 2), 3.75, "1.75 + 2 = 3.75" );
notEqual(addValues( 1, 2), "3", "1 + 2 != '3' as a String");
});
More info:
- How to Test your JavaScript Code with QUnit
- Asserts
- Cookbook
- QUnit, testeando nuestras aplicaciones Javascript
- Getting Started With jQuery QUnit for Client-Side Javascript Testing
###Mocha
Mocha y Jasmine are probably the most popular frameworks. In many cases Jasmine is used to test JS in the client side, and Mocha is used to test JS in the server side (node.js)
##4.- Testing Runners
We can launch the tests from the console with any of the task runners. Thse tests can be launched in a "headless browser" (PhantomJS) or in real browsers (Karma)
We can launch the tests directly from the browser (Jasmine provides its own stand-alone runner to launch the tests)
Karma is a "test runner" we can use to automatize the launch of our tests (written in Jasmine, Mocha o QUnit).
It allow us to execute automatically all our tests in different browsers. Check it in action
It also allow us to integrate our tests with continous integration tools like Jenkins
##5.- E2E testing frameworks
We can raise the test of our project to a higher level and do the so-called functional tests
These test emulte the behaviour of the final user and also test the expected behaviour of our product (no matter the internal code)
describe('angularjs homepage todo list', function() {
it('should add a todo', function() {
browser.get('https://angularjs.org');
element(by.model('todoList.todoText')).sendKeys('write first protractor test');
element(by.css('[value="add"]')).click();
var todoList = element.all(by.repeater('todo in todoList.todos'));
expect(todoList.count()).toEqual(3);
expect(todoList.get(2).getText()).toEqual('write first protractor test');
// You wrote your first test, cross it off the list
todoList.get(2).element(by.css('input')).click();
var completedAmount = element.all(by.css('.done-true'));
expect(completedAmount.count()).toEqual(2);
});
});
We can write and launch these tests with Protractor
Some other tools we can use to launch these tests are Selenium and WebDriver