Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Explain Mocha's testing framework - describe(), it() and before()/etc hooks
// # Mocha Guide to Testing
// Objective is to explain describe(), it(), and before()/etc hooks
// 1. `describe()` is merely for grouping, which you can nest as deep
// 2. `it()` is a test case
// 3. `before()`, `beforeEach()`, `after()`, `afterEach()` are hooks to run
// before/after first/each it() or describe().
//
// Which means, `before()` is run before first it()/describe()
// -----------------------------------------------------------------------------
// should.js is the preferred assertion library
var should = require('should');
// **Only 1 test case (in a nameless test suite)**
it('birds should fly', function(){
/** here.should.be.tested
* However, as long as no error within a it(),
* it() is considered PASSED */
})
// **Only 1 test case, but nested 3-level deep**
// describe() are:
// - commonly known as test suites, which contains test cases
// - merely groups, and you can have groups within groups
describe('galaxy', function(){
describe('earth', function(){
describe('singapre', function(){
it('birds should fly', function(){ /** ... */ })
})
})
})
// **2 test cases in 1 test suite**
// A common scenario.
describe('singapre', function(){
it('birds should fly', function(){ /** ... */ })
it('horse should gallop', function(){ /** ... */ })
})
// **Run once before the first test case**
describe('singapre', function(){
before(function(){
console.log('see.. this function is run ONCE only')
})
it('birds should fly', function(){ /** ... */ })
it('horse should gallop', function(){ /** ... */ })
})
// **Run once before each test case**
describe('singapre', function(){
beforeEach(function(){
console.log('see.. this function is run EACH time')
})
it('birds should fly', function(){ /** ... */ })
it('horse should gallop', function(){ /** ... */ })
})
// **2 test suites in a big test suite**
// A common scenario.
describe('earth', function(){
describe('singapre', function(){
it('birds should fly', function(){ /** ... */ })
})
describe('malaysia', function(){
it('birds should soar', function(){ /** ... */ })
})
})
// **before() can be applied to describe() too**
describe('earth', function(){
before(function(){
console.log('see.. this function is run ONCE only, before first describe()')
})
describe('singapre', function(){
it('birds should fly', function(){ /** ... */ })
})
describe('malaysia', function(){
it('birds should soar', function(){ /** ... */ })
})
})
// **beforeEach() can be applied to describe() too**
describe('earth', function(){
beforeEach(function(){
console.log('see.. this function is run EACH time, before each describe()')
})
describe('singapre', function(){
it('birds should fly', function(){ /** ... */ })
})
describe('malaysia', function(){
it('birds should soar', function(){ /** ... */ })
})
})
@tamlyn

This comment has been minimized.

Copy link

commented Jun 5, 2015

Thanks, this is really helpful. However the last block is incorrect. beforeEach is run before each it, not before each describe. You can see this if you put two its in one of the describes.

@chiragmongia

This comment has been minimized.

Copy link

commented Jul 20, 2015

I have a describe and inside that I've 3 more describe blocks (let's call them children describe blocks). I want to perform some action which would generate a few objects which are common to children describe blocks. Where should the logic for those actions be placed?

@scarfunk

This comment has been minimized.

Copy link

commented Oct 23, 2015

Thanks!

@MJ111

This comment has been minimized.

Copy link

commented Jan 15, 2016

great guide

@ghost

This comment has been minimized.

Copy link

commented Apr 4, 2016

I have often wondered about the name "it". I tried to find out why, but "it" is not a good search term. Finally, a friend pointed out that the first argument to 'it' was meant to complete a sentence starting with "it". Like the following:

describe('some module', function() {
it('should fail gracefully on invalid input', function() {
.
.
.
});
});

So, the test suite is testing (or, I guess, describing) "some module" and "it should fail gracefully on invalid input".

I'm not sure I like this tendency to use natural language in code, but at least I understand the word "it" now.

What I would really like to know is which testing library started this.

@pnkapadia6

This comment has been minimized.

Copy link

commented May 11, 2016

What if we had,

describe('earth', function(){
    beforeEach(function(){
        console.log('see.. this function is run EACH time, before each describe()')
    })
    describe('singapore', function(){
        /** ... */
    })
    describe('malaysia', function(){
        it('birds should soar', function(){ /** ... */ })
        it('birds should eat', function(){ /** ... */ })
    })
})

And I want the beforeEach function to run for every describe & it block

@kaueburiti

This comment has been minimized.

Copy link

commented Aug 20, 2016

Really useful :)

@SergeyKhval

This comment has been minimized.

Copy link

commented Dec 20, 2016

This is a bit misleading. All hooks are run only respective to test specs (it() blocks). You can't execute hooks in respect to describe() blocks.

@ScottFreeCode

This comment has been minimized.

Copy link

commented Mar 14, 2017

Which means, before() is run before first it()/describe()

Nothing runs before describe (except prior describes and code outside of callbacks). Mocha first runs all the describes to find out what all your tests and hooks are, then runs all your hooks and tests after it is done with all the describes and has found all of the tests and hooks. While it seems irrelevant in trivial cases, this is in fact important for several reasons, including variable lifetime/scope and the fact that you cannot add tests or hooks in a test or hook and reliably have Mocha do what you expect.

It would, however, be accurate to say that before runs before the first it in a given describe, as opposed to beforeEach (which in contrast runs again before every it). (In the case of before/beforeEach outside any describe, you have to imagine that all the test files together are implicitly inside one big invisible describe -- Mocha actually handles stuff like hooks outside of describe blocks using something like that.)

@Sairef

This comment has been minimized.

Copy link

commented May 24, 2017

@ScottFreeCode thanks a lot for clear explanation!

@mahantaf

This comment has been minimized.

Copy link

commented Jun 26, 2017

This guide is really good, thank you very much

@tsghosh

This comment has been minimized.

Copy link

commented Jul 24, 2017

@samwize thanks , really helpful

@abhirathmahipal

This comment has been minimized.

Copy link

commented Aug 9, 2017

Thanks 👍

@craneleeon

This comment has been minimized.

Copy link

commented Mar 13, 2018

Very helpful, Thanks.

@ceroloy

This comment has been minimized.

Copy link

commented Mar 28, 2018

Thanks a lot for taking time and writing this, it was really helpful.

@dhawkinson

This comment has been minimized.

Copy link

commented Apr 14, 2018

@ScottFreeCode: That explains some interesting behavior that I have noticed and explains why I have been barking up the wrong tree in trying to solve a timing issue with a hook.

@vgvishesh

This comment has been minimized.

Copy link

commented Jun 14, 2018

Nice

@shuboy2014

This comment has been minimized.

Copy link

commented Jul 20, 2018

Nice information, Thanks :)

@Crosswind

This comment has been minimized.

Copy link

commented Oct 3, 2018

I know this is kinda old but I was wondering if there is way to have a before-hook before the second(third, etc) it. On the first it I'm testing for an empty result (HTTP 204) on the second it I want to have some data in the database. How would that work?

@Choicelin

This comment has been minimized.

Copy link

commented Oct 9, 2018

I have often wondered about the name "it". I tried to find out why, but "it" is not a good search term. Finally, a friend pointed out that the first argument to 'it' was meant to complete a sentence starting with "it". Like the following:

describe('some module', function() {
it('should fail gracefully on invalid input', function() {
.
.
.
});
});

So, the test suite is testing (or, I guess, describing) "some module" and "it should fail gracefully on invalid input".

I'm not sure I like this tendency to use natural language in code, but at least I understand the word "it" now.

What I would really like to know is which testing library started this.

I agree with your opinion.

@Choicelin

This comment has been minimized.

Copy link

commented Oct 9, 2018

it is really a good guide, helps a lot.Thanks!

@jasonk

This comment has been minimized.

Copy link

commented Oct 29, 2018

I have often wondered about the name "it". I tried to find out why, but "it" is not a good search term. Finally, a friend pointed out that the first argument to 'it' was meant to complete a sentence starting with "it". Like the following:

describe('some module', function() {
it('should fail gracefully on invalid input', function() {
.
.
.
});
});

So, the test suite is testing (or, I guess, describing) "some module" and "it should fail gracefully on invalid input".

I'm not sure I like this tendency to use natural language in code, but at least I understand the word "it" now.

What I would really like to know is which testing library started this.

When I first encountered it I wasn't much of a fan either, but it can be really helpful when you get to reporting about your test results. Especially if you go all the way. I try to name tests so that not only does the name itself make a complete sentence (it( 'should do the thing' )) but when combined with it's parent describe blocks it should still make a complete and logical sentence that explains exactly what is being tested:

describe( 'The widget', () => {
  describe( 'when called synchronously', () => {
    it( 'should do the thing' );
  } );
  describe( 'when called asynchronously', () => {
    it( 'should do the thing' );
  } );
} );

That way, when you are generating reports, or lists of failing tests, your reporter can just concatenate the names, and they still show exactly what is being tested (in a way that even Project Managers can understand):

The widget when called synchronously should do the thing   : PASS
The widget when called asynchronously should do the thing  : FAIL
@BarrySaidy

This comment has been minimized.

Copy link

commented Nov 29, 2018

perfect bro...it's clear

@opyate

This comment has been minimized.

Copy link

commented Jan 31, 2019

I agree with @SergeyKhval

This is a bit misleading. All hooks are run only respective to test specs (it() blocks). You can't execute hooks in respect to describe() blocks.

describe('all', function(){
  beforeEach(function(){
    console.log('beforeEach()')
  })
  describe('a', function(){
    it('a1', function(){
      console.log('a1\n')
    })
  })
  describe('b', function(){
    it('b1', function(){
      console.log('b1\n')
    })
    it('b2', function(){
      console.log('b2\n')
    })

    describe('c', function(){
      it('c1', function(){
        console.log('c1\n')
      })
      it('c2', function(){
        console.log('c2\n')
      })
    })
  })
})

Output:

beforeEach()
a1
beforeEach()
b1
beforeEach()
b2
beforeEach()
c1
beforeEach()
c2

So, not before each "describe" as the author said, but before each "it", i.e. before each test.

@vssn

This comment has been minimized.

Copy link

commented May 14, 2019

Mocha offers a synonymous function for describe() which is called context(). It helps to keep an overview over the set of tests especially when nesting describes(). Instead you could write:

describe('feature', () => {

context('when not present', () => {
  it('throws an error', () => {

[...]

@thomashin

This comment has been minimized.

Copy link

commented Jun 10, 2019

Mocha offers a synonymous function for describe() which is called context(). It helps to keep an overview over the set of tests especially when nesting describes(). Instead you could write:

describe('feature', () => {

context('when not present', () => {
  it('throws an error', () => {

[...]

https://mochajs.org/#bdd

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.