Skip to content

Instantly share code, notes, and snippets.

@samwize
Created February 8, 2014 05:53
Show Gist options
  • Save samwize/8877226 to your computer and use it in GitHub Desktop.
Save samwize/8877226 to your computer and use it in GitHub Desktop.
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(){ /** ... */ })
})
})
@jasonk
Copy link

jasonk 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
Copy link

perfect bro...it's clear

@opyate
Copy link

opyate 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
Copy link

vssn 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', () => {

[...]

@shingonu
Copy link

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

@dreusel
Copy link

dreusel commented Jun 16, 2020

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?

You should divide that over 2 describe or context clauses, since you are testing two very different situations. This is exactly what the structure is supposed to point out to you by making it 'not fit' the way you're trying to write it now.

@sanfordstaab
Copy link

What this is not telling me is the scope of variables created in say the before() or describe() functions.
What I want to do is:

  1. create an object
  2. mess with it
  3. check its state
  4. mess with it some more
  5. check its state
    etc.

@tamlyn
Copy link

tamlyn commented Jan 29, 2023

@sanfordstaab there's nothing magic going on with variable scoping, normal JavaScript rules apply. If you create variable in an outer scope, you can modify it in an inner scope then access it in a sibling scope.

@sanfordstaab
Copy link

Ah, yes, I eventually figured that out but what I noticed is that each it() and apparently describe() is called sequentially by mocha in an apparently asynchronous fashion, which I imagine is necessary to support async tests. Things happen in order but the flow is not linear.
Anyway, I am now wondering how to do UI testing that requires user verification and interaction - like mouse clicks on rendered HTML. I am researching that now and considering using alert() or confirm() messages to do that. Any suggestions are welcome and thanks for your examples here!

@waveo-wangxiao
Copy link

Hi, I am new to mocha, may I know if I could put 'describe' in 'it'? Something like below:

let data: string[]

before('prepare data', ()=> {
	return new Promise((resolve: any) => {
		setTimeout(()=> {
			data = ['a', 'b'];
			resolve();
		}, 500)
	});
});

it("test", function () {
	describe("data", function () {
		data?.forEach((m) => {
			it(`${m}`, function () {
				assert(m, 'not empty');
			});
		})
	})
});

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