Mohammed Erraysy
A Frontend enthusiast making slides.
TDD is when your development is test first.
In other words, it's when you write tests and make them fail first, then start implementing them in your code to make them pass, then you can refactor safely.
// Calc.js
// A super Calculator
function Calc(opts) {
var initialValue = opts.initialValue || 0;
var value = initialValue;
}
// Calc.test.js
// TDD
suite('Calc') {
var calc = new Calc();
}
test('add a num') {
calc.add(2);
assert.equal(calc.getValue(), 2);
}
test('subtract a num') {
calc.subtract(1);
assert.equal(calc.getValue(), 1);
}
test('clear the value') {
calc.clear();
assert.equal(calc.getValue(), 0);
}
this.add = function (num) {
value += num;
return;
};
this.subtract = function (num) {
value -= num;
return;
};
this.getInitialValue = function () {
return initialValue;
};
this.getValue = function () {
return value;
};
this.reset = function reset() {
value = initialValue;
return;
};
BDD is an approach to tests that solves some problems that might occur with TDD, it means to test against scenarios (behavior) instead of implementations.
// Calc.test.js
// BDD
describe('Calc') {
var calc;
beforeEach() {
calc = new Calc({
initialValue: 2,
});
}
it('should add a num') {
calc.add(2);
expect(calc.getValue()).to.equal(calc.getInitialValue() + 2);
}
it('should subtract a num') {
calc.subtract(1);
expect(calc.getValue()).to.equal(calc.getInitialValue() - 1);
}
it('should clear the value') {
calc.add(1);
calc.clear();
expect(calc.getValue()).to.equal(calc.getInitialValue());
}
}
// Mocha Framework
describe('Array', function() {
before(function() {
// ...
});
describe('#indexOf()', function() {
context('when not present', function() {
it('should not throw an error', function() {
// assertions
});
it('should return -1', function() {
// assertions
});
});
context('when present', function() {
it('should return the index of the element', function() {
// assertions
});
});
});
});
// Mocha Framework
describe('Array', function() {
before(function() {
// ...
});
describe('#indexOf()', function() {
context('when not present', function() {
it('should not throw an error', function() {
});
it('should return -1', function() {
});
});
});
});
(function() {
[1,2,3].indexOf(4);
}).should.not.throw();
[1,2,3].indexOf(4).should.equal(-1);
Mocha | Karma | Jest | AVA | |
---|---|---|---|---|
AIO | fw | no | yes | yes |
EOC | simple | a little bit cheesy | very easy | n/a |
DOCS | website not bad |
website good |
website excellent |
github excellent |
- AIO (All in One): does the runner provide also a framework and assertions...etc
- EOC (Ease of Configuration): how easy is it to be configured.
- DOCS: how is the documentation, is it easily accessible.
Mocha | Jasmine | Tape | QUnit | |
---|---|---|---|---|
AIO | no | yes | asserts | yes |
EOC | simple | simple | n/a | n/a |
DOCS | website not bad |
website good |
github not bad |
website very good |
BDD | yes | yes | no | no |
- AIO (All in One): does the framework provides other features, assertions...etc
- EOC (Ease of Configuration): how easy is it to be configured.
- DOCS: how is the documentation, is it easily accessible.
Chai | Should | Expect | Must | |
---|---|---|---|---|
DOCS | website excellent |
website excellent |
github not bad |
github good |
BDD | yes | yes | yes | yes |
ASYNC | plugins | yes | yes | yes |
- DOCS: how is the documentation, is it easily accessible.-
- ASYNC: does the library support assertions on promises, async code.
// chai should
chai.should();
foo.should.be.a('string');
foo.should.equal('bar');
foo.should.have.lengthOf(3);
tea.should.have.property('flavors').with.lengthOf(3);
// chai expect
var expect = chai.expect;
expect(foo).to.be.a('string');
expect(foo).to.equal('bar');
expect(foo).to.have.lengthOf(3);
expect(tea).to.have.property('flavors').with.lengthOf(3);
// chai assert
var assert = chai.assert;
assert.typeOf(foo, 'string');
assert.equal(foo, 'bar');
assert.lengthOf(foo, 3)
assert.property(tea, 'flavors');
assert.lengthOf(tea.flavors, 3);
(5).should.be.exactly(5).and.be.a.Number();
should(5).be.exactly(5).and.be.a.Number();
(10).should.not.be.a.Promise();
expect(1).to.be.ok();
expect({ a: 'b' }).to.have.key('a');
expect([]).to.be.empty();
expect(1).to.eql('1');
expect(program.version).to.match(/[0-9]+\.[0-9]+\.[0-9]+/);
[].must.be.empty();
{}.must.have.nonenumerable("foo");
(42).must.be.above(13);
Promise.resolve(42).must.then.equal(42);
Promise.resolve([1, 2, 3]).must.eventually.not.include(42);
Promise.reject(new Error("Problemo")).must.reject.with.error(/problem/i);
Sinon | Test double | |||
---|---|---|---|---|
DOCS | website excellent |
github good |
github not bad |
github bad |
FF | yes | yes | no | no |
- DOCS: how is the documentation, is it easily accessible.-
- FF: is it full featured ? mocks, spies, stubs ...etc.
// Spy
function callOnce(fn) {
var called = false;
return function() {
if (!called) fn.apply(this);
called = true;
};
}
it('calls the passed function', function () {
var callback = sinon.spy();
var proxy = callOnce(callback);
proxy();
assert(callback.called); // pass
});
it('calls the passed function once', function () {
var callback = sinon.spy();
var proxy = callOnce(callback);
proxy();
proxy();
assert(callback.calledOnce); // pass
});
// Ajax
function getTodoItems(id, callback) {
jQuery.ajax({
url: '/todo/' + id + '/items',
success: function (data) {
callback(data);
}
});
}
it('makes a GET request for todo items', function () {
sinon.stub(jQuery, 'ajax');
getTodoItems(42, sinon.spy());
assert(jQuery.ajax.calledWithMatch({ url: '/todo/42/items' })); // pass
});
// Ajax
function defer(fn) {
var timeout;
return function () {
clearTimeout(timeout);
timeout = setTimeout(fn, 100);
};
}
it('it calls the passed function only after 100ms', function () {
var clock = sinon.useFakeTimers();
var fn = sinon.spy();
var proxy = defer(fn);
proxy();
clock.tick(99);
assert(fn.notCalled); // pass
clock.tick(100);
assert(fn.calledOnce); // pass
});
All in one, simple, easy to config and it's backed and used by folks at Facebook.
Have more features, yet still simple and they are very popular.
This is a nice combination to consider as well, I've tested it with AngularJS and I was ok with it.
By Mohammed Erraysy
An Introduction to Code Testing with a quick overview of some of the available JavaScript testing tools