-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
bef1c9f
commit ad4e9aa
Showing
1 changed file
with
28 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,23 +5,22 @@ description: How to test your CLI | |
|
||
Testing in oclif can be done with any testing framework. You can run commands with `MyCommand.run()` which returns a promise you can wait on. | ||
|
||
There are common tasks however when writing CLI tools. For this, we have a conventional set of tools that we suggest using to test your CLI. These are based on [mocha](https://mochajs.org) and [fancy-test](https://github.com/jdxcode/fancy-test). | ||
There are common tasks however when writing CLI tools. For this, we have [@oclif/test](https://github.com/oclif/test), which provides a [conventional set of utilities](https://github.com/oclif/test?tab=readme-ov-file#usage) that we find useful for testing oclif CLIs. | ||
|
||
Mocha is the top JavaScript testing framework and a solid choice for any project. fancy-test is a tool we developed that builds on top of mocha to make it easy to repeat patterns and write concise mocha tests. There is also a library [@oclif/test](https://github.com/oclif/test) that extends fancy-test with helpers specific to testing oclif CLIs. These are things like running a command or hook or checking if an exit status code is set, for example. | ||
Any CLI built with oclif will come preloaded with [mocha](https://www.npmjs.com/package/mocha) (our preferred testing framework but you're free to use whatever you prefer) and [@oclif/test](https://github.com/oclif/test) as well as an example test that should work out of the box with `npm test` or `yarn test`. | ||
|
||
Any CLI built with oclif will come preloaded with these tools and an example test that should work out of the box with `npm test` or `yarn test`. | ||
|
||
As an example, let's look at the `heroku whoami` command which makes an API call to get the current logged in user. If the user is not logged in, it exits with status 100. (This is a simplified example, here is [the actual code](https://github.com/heroku/heroku-cli-plugin-auth).) | ||
As an example, let's look at this `whoami` command which makes an API call to get the current logged in user. If the user is not logged in, it exits with status 100. | ||
|
||
**src/commands/whoami.ts** | ||
|
||
```js | ||
import Command from '@heroku-cli/command' | ||
import {Command} from '@oclif/core' | ||
|
||
export class Whoami extends Command { | ||
async run() { | ||
try { | ||
let {body: account} = await this.heroku.get('/account') | ||
let {body: account} = await this.api.get('/account') | ||
this.log(account.email) | ||
} catch (err) { | ||
if (err.statusCode === 401) { | ||
|
@@ -40,43 +39,34 @@ Here is the test code | |
**test/commands/whoami.test.ts** | ||
|
||
```typescript | ||
import {expect, test} from '@oclif/test' | ||
|
||
describe('auth:whoami', () => { | ||
test | ||
.nock('https://api.heroku.com', api => api | ||
.get('/account') | ||
// user is logged in, return their name | ||
.reply(200, {email: '[email protected]'}) | ||
) | ||
.stdout() | ||
.command(['auth:whoami']) | ||
.it('shows user email when logged in', ctx => { | ||
expect(ctx.stdout).to.equal('[email protected]\n') | ||
import {runCommand} from '@oclif/test' | ||
import {expect} from 'chai' | ||
import nock from 'nock' | ||
|
||
describe('whoami', () => { | ||
it('shows user email when logged in', async () => { | ||
nock('https://api.example.com') | ||
.get('/account') | ||
// user is logged in, return their name | ||
.reply(200, {email: '[email protected]'}) | ||
|
||
const {stdout} = await runCommand('whoami') | ||
expect(stdout).to.equal('[email protected]') | ||
}) | ||
|
||
test | ||
.nock('https://api.heroku.com', api => api | ||
.get('/account') | ||
// HTTP 401 means the user is not logged in with valid credentials | ||
.reply(401) | ||
) | ||
.command(['auth:whoami']) | ||
// checks to ensure the command exits with status 100 | ||
.exit(100) | ||
.it('exits with status 100 when not logged in') | ||
it('exits with status 100 when not logged in', async () => { | ||
nock('https://api.example.com') | ||
.get('/account') | ||
// HTTP 401 means the user is not logged in with valid credentials | ||
.reply(401) | ||
|
||
const {error} = await runCommand('whoami') | ||
expect(error?.oclif?.exit).to.equal(100) | ||
}) | ||
}) | ||
``` | ||
|
||
These tools are setup to not only mock out the stdout/stderr and HTTP calls, but they're setup to ensure they automatically reset after the test. A common issue we've had when building CLIs with simpler `beforeEach/afterEach` filters is that if the `afterEach` filters aren't setup correctly, a failing test can leave mocks around that make later tests fail. Using fancy-test, we avoid this problem and only have to declare our mocks once. | ||
|
||
For more on how to test with oclif, check out the docs for [fancy-test](https://github.com/jdxcode/fancy-test) and [@oclif/test](https://github.com/oclif/test). | ||
|
||
## stdout/stderr | ||
|
||
The stdout/stderr mocks use [stdout-stderr](https://github.com/jdxcode/stdout-stderr) under the hood. This library can be used standalone if you'd prefer to use jest or want a different testing setup but still have the ability to mock out stdout and stderr. | ||
|
||
If you want to see the output but leave it mocked, you can either pass in `{print: true}` to the options, or set `TEST_OUTPUT=1`. | ||
For more on how to test with oclif, check out the docs for [@oclif/test](https://github.com/oclif/test). | ||
|
||
## Code Coverage | ||
|
||
|