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);
});
});
});
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