Custom ESLint rules #4/5: how to test custom rules
No pineapple pizza must be allowed in our code. That is why, in the previous bite, we created a custom ESLint plugin with a dedicated rule to forbid it. It's now time to test it!
Create ESLint plugin unit tests
Install eslint
as devDependency
and create the test file and test entry point file.
yummy-pizza $ npm i eslint -D -w eslint-plugin-pizza
yummy-pizza $ touch ./packages/eslint-plugin-pizza/rules/no-pineapple-pizza.test.js ./packages/eslint-plugin-pizza/pizza-rules-test-entrypoint.js
Your layout should look like this:
yummy-pizza/
├─ packages/
│ ├─ eslint-plugin-pizza/
│ │ ├─ rules/
│ │ │ ├─ no-pineapple-pizza.js
│ │ │ ├─ no-pineapple-pizza.test.js
│ │ ├─ pizza-rules-test-entrypoint.js
│ │ ├─ package.json
│ ├─ app/
│ │ ├─ src/
│ │ ├─ package.json
├─ package.json
no-pineapple-pizza.test.js
is our test filepizza-rules-test-entrypoint.js
is the file that will be executed bynpm t
command and that will run our tests
Set up test script in ESLint plugin's package.json
to call our entry point.
// yummy-pizza/eslint-plugin-pizza/package.json
{
"name": "eslint-plugin-pizza",
"version": "1.0.0",
"description": "Custom ESLint plugin for pizza",
"type": "module",
"main": "index.js",
"scripts": {
"test": "node ./pizza-rules-test-entrypoint.js"
},
"author": "Gabriele Buffolino",
"license": "ISC",
"devDependencies": {
"eslint": "^8.53.0"
}
}
Our test entry point file will just import
our test files one by one and that's it! It's job is done!
// yummy-pizza/eslint-plugin-pizza/pizza-rules-test-entrypoint.js
import "./rules/no-pineapple-pizza.test.js";
Now we need to define our rule test. Be sure to refer to official docs, but the main gotchas are:
you need a
RuleTester
objectyou need to invoke its
run
method passing inthe rule name
the rule code that should be tested
an object containing what code must be considered valid and what invalid
for fixable rules, we need to provide also how we expect the invalid code to be fixed
// yummy-pizza/packages/eslint-plugin-pizza/rules/no-pineapple-pizza.test.js
import rule from "./no-pineapple-pizza.js";
import { RuleTester } from "eslint";
const ruleTester = new RuleTester({
// Must use at least ecmaVersion 2015 because
// that's when `const` was introduced.
parserOptions: { ecmaVersion: 2015 },
});
ruleTester.run(
"no-pineapple-pizza", // rule name
rule, // rule code
{
valid: [
{
// valid code
code: "const yummyPizza = eatPizzaMargherita();",
},
],
// 'invalid' checks cases that should not pass
invalid: [
{
// code that should be marked as error
code: "const yummyPizza = eatPineapplePizza();",
// what should we expect as fix
output: "const yummyPizza = eatPizzaMargherita();",
// one error should be detected
errors: 1,
},
],
}
);
// share our joy for success
console.log(`[SUCCESS] no-pineapple-pizza`);
Final step: define test
script in the root package.json
. Using --workspaces
and --if-present
combined, we will run all packages test scripts and won't error if not present.
// yummy-pizza/package.json
{
"name": "yummy-pizza",
"version": "1.0.0",
"description": "Sample of custom ESLint plugin definition",
"scripts": {
"test": "npm run test --workspaces --if-present"
},
"author": "Gabriele Buffolino",
"license": "ISC",
"workspaces": [
"packages/*"
]
}
Run npm t
(short for npm run test
) and...
yummy-pizza $ npm t
> yummy-pizza@1.0.0 test
> npm t --workspaces --if-present
> eslint-plugin-pizza@1.0.0 test
> node ./pizza-rules-test-entrypoint.js
[SUCCESS] no-pineapple-pizza
...it works!
In the next bite, we will have a look at how to install our plugin and use it across our application.
References
Subscribe to my newsletter
Read articles from Gabriele Buffolino directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Gabriele Buffolino
Gabriele Buffolino
A collaborative Front End Software Developer with 17 years of experience, including 7 years focused on cutting-edge web development. Driven by a passion for continual learning.