Testing Command Line Tools
shadowtime2000
Posted on October 18, 2020
Overview
I think you all know the popular library for creating CLIs called yargs. That is what we are going to be using. Our CLI should reverse a string.
$ reverse reverse --string string
gnirts
Setup
Create a folder for your project. Then run these commands inside of it.
$ npm init -y
$ npm install -D typescript @types/yargs @types/node
$ npm install --save yargs
Make sure you set the bin
attribute of your package.json
to dist/cli.js
and the main
to dist/index.js
. Make your tsconfig.json
look like this:
{
"compilerOptions": {
"esModuleInterop": true,
"module": "CommonJS",
"moduleResolution": "node",
"outDir": "./dist",
"target": "ESNext"
},
"exclude": ["node_modules", "**/*.spec.ts"],
"include": ["src/**/*"]
}
Creating The CLI
Inside src/cli.ts
, write this:
#!/usr/bin/env node
import yargs from "yargs";
yargs
.scriptName("reverse")
.usage("$0 <cmd> [args]")
.command(
"reverse [string]",
"reverse the string",
(y) => {
y.positional("string", {
type: "string",
default: "string",
describe: "string to reverse",
});
},
(argv) => {
console.log(argv.string.split("").reverse().join(""));
}
)
.help().argv;
and now you have a working CLI!
Unit Testing API
First before we create actual tests, we need to change the structure of the project. Create a file called src/index.ts
and put this inside of it:
export function reverseString(str: string) {
return str.split("").reverse().join("");
}
Inside of src/cli.ts
add an import statement to the top to import reverseString
from index.ts
and change the callback to do this:
console.log(reverseString((argv.string as string)));
So now our CLI has the structure to support unit testing!
So now run these commands:
$ npm install -D mocha chai
Also, set your test
script to tsc && mocha test/**/*.js
.
Now under test/api/reverseString.spec.js
write this:
const { expect } = require("chai");
const { reverseString } = require("../../dist/index");
describe(".reverseString", () => {
it("should reverse properly", () => {
expect(reverseString("foo")).to.equal("oof");
});
});
But, this really isn't testing the actual CLI, just the API under it.
Testing the CLI
Under test/cli/reverse.spec.js
write this:
const { expect } = require("chai");
const { execSync } = require("child_process");
const test = (args) => {
return execSync(`node dist/cli.js reverse ${args}`).toString();
};
describe("CLI", () => {
it("should use the positional argument", () => {
expect(test("--string foo")).to.equal("oof\n");
});
it("should use the non positional argument", () => {
expect(test("foo")).to.equal("oof\n");
});
});
This is probably the worst way to test it, so if you have a better way, feel free to put it in the comments.
Posted on October 18, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.