The purpose of this style guide is to offer suggested best practices when writing Jasmine unit tests.
This style guide ultimately represents the opinions of its contributors. If you disagree with anything, or wish to add more, please create an issue or submit a pull request. Our goal is to continuously improve the guide and build consensus around it.
this
Is How We Do ItAll
sdescribe
tiveLabel your test suites (describe
blocks) and specs (it
blocks) in a way that clearly conveys the intention of each unit test. Note that the name of each test is the title of its it
preceded by all its parent describe
names. Favor assertive verbs and avoid ones like "should."
// Output: "Array adds to the end"
describe('Array', function() {
it('adds to the end', function() {
var initialArray = [1];
initialArray.push(2);
expect(initialArray).toEqual([1, 2]);
});
});
// Output: "Array.prototype .push(x) appends x to the end of the Array"
describe('Array.prototype', function() {
describe('.push(x)', function() {
it('appends x to the end of the Array', function() {
var initialArray = [1];
initialArray.push(2);
expect(initialArray).toEqual([1, 2]);
});
});
});
A unit test should test one thing. Confine your it
blocks to a single assertion.
describe('Array.prototype', function() {
describe('.push(x)', function() {
it('appends x to the end of the Array and returns it', function() {
var initialArray = [1];
expect(initialArray.push(2)).toBe(2);
expect(initialArray).toEqual([1, 2]);
});
});
});
describe('Array.prototype', function() {
describe('.push(x)', function() {
it('appends x to the end of the Array', function() {
var initialArray = [1];
initialArray.push(2);
expect(initialArray).toEqual([1, 2]);
});
it('returns x', function() {
var initialArray = [1];
expect(initialArray.push(2)).toBe(2);
});
});
});
Organize your code in a way that clearly conveys the 3 A's of each unit test. One way to accomplish this is by Arranging and Acting in before
blocks and Asserting in it
ones.
describe('Array.prototype', function() {
describe('.push(x)', function() {
it('appends x to the end of the Array', function() {
var initialArray = [1];
initialArray.push(2);
expect(initialArray).toEqual([1, 2]);
});
});
});
describe('Array.prototype', function() {
describe('.push(x)', function() {
var initialArray;
beforeEach(function() {
initialArray = [1]; // Arrange
initialArray.push(2); // Act
});
it('appends x to the end of the Array', function() {
expect(initialArray).toEqual([1, 2]); // Assert
});
});
});
Use before
/after
blocks to DRY up repeated setup, teardown, and action code.
describe('Array.prototype', function() {
describe('.push(x)', function() {
it('appends x to the end of the Array', function() {
var initialArray = [1];
initialArray.push(2);
expect(initialArray).toEqual([1, 2]);
});
it('returns x', function() {
var initialArray = [1];
expect(initialArray.push(2)).toBe(2);
});
});
});
describe('Array.prototype', function() {
describe('.push(x)', function() {
var initialArray,
pushResult;
beforeEach(function() {
initialArray = [1];
pushResult = initialArray.push(2);
});
it('appends x to the end of the Array', function() {
expect(initialArray).toEqual([1, 2]);
});
it('returns x', function() {
expect(pushResult).toBe(2);
});
});
});
this
Is How We Do ItUse this
to share variables between it
and before
/after
blocks.
this
object between specs to avoid state leakdescribe('Array.prototype', function() {
describe('.push(x)', function() {
var initialArray,
pushResult;
beforeEach(function() {
initialArray = [1];
pushResult = initialArray.push(2);
});
it('appends x to the end of the Array', function() {
expect(initialArray).toEqual([1, 2]);
});
it('returns x', function() {
expect(pushResult).toBe(2);
});
});
});
describe('Array.prototype', function() {
describe('.push(x)', function() {
beforeEach(function() {
this.initialArray = [1];
this.pushResult = this.initialArray.push(2);
});
it('appends x to the end of the Array', function() {
expect(this.initialArray).toEqual([1, 2]);
});
it('returns x', function() {
expect(this.pushResult).toBe(2);
});
});
});
All
sPrefer beforeEach/afterEach
blocks over beforeAll/afterAll
ones. The latter are not reset between tests.
All
block execution relative to Each
ones is not always obviousdescribe('Array.prototype', function() {
describe('.push(x)', function() {
beforeAll(function() {
this.initialArray = [1];
});
beforeEach(function() {
this.pushResult = this.initialArray.push(2);
});
it('appends x to the end of the Array', function() {
expect(this.initialArray).toEqual([1, 2]);
});
it('returns x', function() {
expect(this.pushResult).toBe(2);
});
});
});
describe('Array.prototype', function() {
describe('.push(x)', function() {
beforeEach(function() {
this.initialArray = [1];
this.pushResult = this.initialArray.push(2);
});
it('appends x to the end of the Array', function() {
expect(this.initialArray).toEqual([1, 2]);
});
it('returns x', function() {
expect(this.pushResult).toBe(2);
});
});
});
describe
tiveNest describe
blocks liberally to create functional subsets.
describe('Array.prototype', function() {
describe('.push(x) on an empty Array', function() {
beforeEach(function() {
this.initialArray = [];
this.initialArray.push(1);
});
it('appends x to the Array', function() {
expect(this.initialArray).toEqual([1]);
});
});
describe('.push(x) on a non-empty Array', function() {
beforeEach(function() {
this.initialArray = [1];
this.initialArray.push(2);
});
it('appends x to the end of the Array', function() {
expect(this.initialArray).toEqual([1, 2]);
});
});
});
describe('Array.prototype', function() {
describe('.push(x)', function() {
describe('on an empty Array', function() {
beforeEach(function() {
this.initialArray = [];
this.initialArray.push(1);
});
it('appends x to the Array', function() {
expect(this.initialArray).toEqual([1]);
});
});
describe('on a non-empty Array', function() {
beforeEach(function() {
this.initialArray = [1];
this.initialArray.push(2);
});
it('appends x to the end of the Array', function() {
expect(this.initialArray).toEqual([1, 2]);
});
});
});
});
If appropriate, use Jasmine's built-in matchers (such as toContain
, jasmine.any
, jasmine.stringMatching
, ...etc) to compare arguments and results. You can also create your own matcher via the asymmetricMatch
function.
describe('Array.prototype', function() {
describe('.push(x)', function() {
beforeEach(function() {
this.initialArray = [];
this.initialArray.push(1);
});
it('appends x to the Array', function() {
expect(this.initialArray).toEqual([1]);
});
});
});
describe('Array.prototype', function() {
describe('.push(x)', function() {
beforeEach(function() {
this.initialArray = [];
this.initialArray.push(1);
});
it('appends x to the Array', function() {
expect(this.initialArray).toContain(1);
});
});
});