Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple before() or beforEach() in mocha

I need to do multiple async calls in before() hook in mocha. I need to delete a user, then signup , verify email and finally login to get token to authenticate all other test cases. Here is my code snippet :

    const userInfo = {
        "password": "pass123",
        "email": "[email protected]",
    };

    var token = '' , userId = '';

    before((done) => {
        // Delete the  user if already exists
        User.remove({
                email: userInfo.email
            }).then((res) => {
              //  console.log(res.result);
            })
            .end(done);
        done();
    });


    before((done) => {
        request(app)
            .post('/api/users')        
            .send(userInfo)
            .expect(200)
            .expect((res) => {        
            })
            .end(done);
    });

    before((done) => {
        User.findOne({
                email: userInfo.email
            }).then((res) => {            
                userId = res._id;
                request(app)
                    .post('/api/users/verify-email')
                    .send({'token' : userId})
                    .expect(200)
                    .expect((res) => {                
                    })
                    .end(done);
            })
            .end(done);
        done();
    });

Here these calls are not executing in sequence. I need to fetch userId before verifying the email, but I am getting below error:

POST /api/users/verify-email 401 4.082 ms - 45
    1) "before all" hook
like image 378
Girish Gupta Avatar asked Oct 26 '25 10:10

Girish Gupta


1 Answers

First of all, yes, mocha allows multiple before hooks and guarantees that they are called in the right order. To make sure of this you could run this snippet.

'use strict';

const results = [];
const assert = require('assert');

before(done => {
    setTimeout(() => {
        console.log(`First 'before'`);
        results.push(1);
        done(); //Will be called last
    }, 1000)
});

before(done => {
    setTimeout(() => {
        console.log(`Second 'before'`);
        results.push(2); //Will be called second
        done();
    }, 300)
});

before(done => {
    setTimeout(() => {
        console.log(`Third 'before'`);
        results.push(3); //Will be called first
        done();
    }, 100)
});

describe('Before hooks order', () => {
    it('should before hooks sequentially', () => {
        //Check if the hooks were called in the right order anyway
        assert.deepEqual(results, [1, 2, 3]);
    });
});
//Output is:
// First 'before'
// Second 'before'
// Third 'before'

But to make this happen you need to call done() only when all the async operations are done to let mocha know that the hook is completed and it should run the next one.

Also there is a rule that any Node.js callback must be called only once. So here are a couple of fixes:

before((done) => {
    // Delete the  user if already exists
    User
        .remove({
            email: userInfo.email
        })
        .then((res) => {
            //  console.log(res.result);
        })
        .end(done);
    //Do not call done here() because User.remove have only started
    //We need to wait until it finishes. Done will be called in .end method
    // done();
});

before((done) => {
    //All good here
    request(app)
        .post('/api/users')
        .send(userInfo)
        .expect(200)
        .expect((res) => {
        })
        .end(done);
});

before((done) => {
    User.findOne({
        email: userInfo.email
    }).then((res) => {
        userId = res._id;
        //You need a return statement here so the outer promise waits
        //Until the internal call finishes
        return request(app)
            .post('/api/users/verify-email')
            .send({'token': userId})
            .expect(200)
            .expect((res) => {
            });
    //We must not call callback multiple times, so remove this.
    // .end(done);
    })
    //Now this 'end' will happen only after both calls finish
    .end(done);

    //Remove this please, for the same reason as with the first 'before'
    // done();
});

Please check it out. I'm not able to run your code (don't have your api), so please let me know of any problems.

like image 173
Antonio Narkevich Avatar answered Oct 29 '25 00:10

Antonio Narkevich



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!