'Enforce one describe per file
The Story:
We have a rather big test codebase with Protractor+Jasmine tests.
One of the current problems we have is that some of the test/spec files contain more than one describe which causes troubles from time to time - for example, when debugging tests one by one (or in batches) we use fdescribe
/fit
; and occasionally we don't notice that there are other decribe
s in the same file at the bottom eventually leading to parts of the tests to be unintentionally skipped.
In other words, this is sort of a variation of "one assertion per test" type of a rule helping to keep the test codebase clean and "flat".
The Question:
Is there a way to prohibit having more than one describe
per file? I am currently thinking about approaching it with static code analysis and ESLint
, but I'm open to other solutions as well.
Samples:
Example of a violation:
describe("Test 1", function () {
it("should do something", function () {
expect(true).toBe(true);
});
});
describe("Test 2", function () {
it("should do something else", function () {
expect(false).toBe(false);
});
});
If there is a single describe
block, but it contains nested describe
s, it should not be reported as a violation. In other words, this is okay to have:
describe("Test 1", function () {
it("should do something", function () {
expect(true).toBe(true);
});
describe("Test 2", function () {
it("should do something else", function () {
expect(false).toBe(false);
});
});
});
Solution 1:[1]
The tricky part is flagging only describe blocks that are not nested, or "top-level" describes. Luckily, this is totally possible with ESLint!
ESLint "visits" the nodes two times while traversing the abstract syntax tree (AST, for short) of your JavaScript code: once while going down the tree, and another while going back up.
The tree is traversed depth first, so if for example you have 3 describe
blocks in your code like this:
describe("Test 1", function () {
it("should do something", function () {
expect(true).toBe(true);
});
describe("Test 2", function () {
it("should do something else", function () {
expect(false).toBe(false);
});
});
});
describe("Test 3", function () {
it("should do something", function () {
expect(true).toBe(true);
});
});
The nodes would be visited in the following order:
enter "Test 1"
-> enter "Test 2"
-> exit "Test 2"
-> exit "Test 1"
-> enter "Test 3"
-> exit "Test 3"
That means we just have to keep track of all the describe
calls in a stack while going "down" a subtree, then pop them one at a time while going "up" that subtree. if while going up there is only one node left to pop from the stack, then that node is a "top-level" describe
.
At the end, if we found more than on "top-level" describe, then our rule should report an error. I whipped up a small working prototype for you: https://astexplorer.net/#/3vMUwQjfpD/2
Solution 2:[2]
With the help of @vitorbal's sample code for the rule, the pull request with the new max-top-level-suites
rule is now merged into and is a part of eslint-plugin-mocha
ESLint plugin.
Solution 3:[3]
You can use no-restricted-syntax rule.
The selector that you want to use is: Program > ExpressionStatement[expression.type='CallExpression'][expression.callee.type='Identifier'][expression.callee.name='describe'] ~ ExpressionStatement[expression.type='CallExpression'][expression.callee.type='Identifier'][expression.callee.name='describe']
Complete example:
"no-restricted-syntax": [2,
{
"selector": "Program > ExpressionStatement[expression.type='CallExpression'][expression.callee.type='Identifier'][expression.callee.name='describe'] ~ ExpressionStatement[expression.type='CallExpression'][expression.callee.type='Identifier'][expression.callee.name='describe']",
"message": "Only one top level describe per test file"
}
],
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | vitorbal |
Solution 2 | alecxe |
Solution 3 | pauldcomanici |