faq labelnode node_modules/.bin/mocha --version(Local) and mocha --version(Global). We recommend avoiding the use of globally installed Mocha.Is it possible to add a top level hook from within a describe callback?
Is there a way to modify the add_top_level_hook function below to achieve my desired behaviour?
function add_top_level_hook() {
after('top level after', function() {
console.log('top level after');
});
};
describe('some suite', function() {
add_top_level_hook();
after('some suite after', function() {
console.log('some suite after');
});
it('some test', function() {});
});
describe('some other suite', function() {
after('some other suite after', function() {
console.log('some other suite after');
});
it('some other test', function() {});
});
Desired behaviour: [What you expect to happen]
I would like the top level after to fire after hooks in some suite and some other suite
so that the output of running the code with mocha would be:
some suite
✓ some test
some suite after
some other suite
✓ some other test
some other suite after
top level after
Note that if we wrap the suites above with another describe, I would like the top level after to fire after the hooks of that parent suite as well.
Actual behavior: [What actually happens]
Currently I get the following output:
some suite
✓ some test
top level after
some suite after
some other suite
✓ some other test
some other suite after
Reproduces how often: [What percentage of the time does it reproduce?]
mocha --version and node node_modules/.bin/mocha --version:node --version:If I understand your question correctly, all you have to do is not wrap your "top level after" in a function:
```
after('top level after', function() {
console.log('top level after');
});
describe('some suite', function() {
after('some suite after', function() {
console.log('some suite after');
});
it('some test', function() {});
});
describe('some other suite', function() {
after('some other suite after', function() {
console.log('some other suite after');
});
it('some other test', function() {});
});```
If I am not mistaken, you are looking for something like this (using after.first):
function add_top_level_hook() {
after.first('top level after', function() { // this hook will be the very first after hook to run
console.log('top level after');
});
};
describe('some suite', function() {
add_top_level_hook();
after('some suite after', function() {
console.log('some suite after');
});
it('some test', function() {});
});
describe('some other suite', function() {
after('some other suite after', function() {
console.log('some other suite after');
});
it('some other test', function() {});
});
@patrickptm what you are suggesting indeed results in desired output, however the whole point of this issues is how to achieve this output regardless of where that code that creates the before hook is called from.
@ORESoftware after.first your solution does work for me:
after.first('top level after', function() {
^
TypeError: after.first is not a function
@adrian-gierakowski thanks for the detailed issue. I understand what you are seeking, but I am not sure why you would need this feature.
Currently the test scope is determined at runtime when running the suites so this behavior cannot be achieved.
If you are trying to achieve abstraction, you can do the following:
function afterAllHook() {
console.log('after all')
}
describe("Some Test", function() {
it('Logging Test', function() {
console.log('hi!');
})
after(function() {
console.log('suite after')
})
after(afterAllHook)
})
This will log:
hi!
suite after
after all
the workaround I have come up with is to create a module like so ( example in coffeescript ):
cleanup_thunks = []
after "global cleanup", ->
if cleanup_thunks.length > 0
Promise.all cleanup_thunks.map ( thunk ) -> thunk()
module.exports = ( cleanup_thunk ) ->
cleanup_thunks.push cleanup_thunk
and add it to mocha.opts:
--require coffeescript/register
--file lib/mocha_global_cleanup.coffee
Which causes the after hook to be added to root suite
Now from any test file I can do:
mocha_global_cleanup = require "../lib/mocha_global_cleanup"
descrive "some suite", ->
mocha_global_cleanup -> some_clenup_code()
before ->
mocha_global_cleanup -> some_more_clenup_code()
it "some test", ->
mocha_global_cleanup -> can_even_add_cleanup_code_from_within_test_cases()
In case you wonder about the use case: its for cleaning up shared resources which are lazily initialised by the suitestests that need them. For example a database connection, which might be used\shared by multiple ( but not all ) tests\suites. The resource is initialised only once, and stays available until all tests are finished.
@Bamieh the problem with your suggestion is that if you have another describe section after to the one in which you call after(afterAllHook), the afterAllHook hook will be called before the tests\hooks in that section. As explained above, what I want it to execute some code after all tests\suites have finished, but I also want to be able to decide what code is executed from anywhere in the test suite.
@adrian-gierakowski your code is actually the solution to the issue not a workaround.
You can access the title and other information using this, notice that this is not intended for common use.
const teardownSteps = [];
function addToTeardown(suite, conn) {
teardownSteps.push({title: suite.title, conn});
}
after(function() {
teardownSteps.map(teardown => {
// ...teardown for each connection
})
})
describe("Some Test", function() {
it('Logging Test', function() {
console.log('hi!');
})
addToTeardown(this, 'db clear');
})
closing since I have found a satisfying solution