Skip to content

CLI: basic unit tests #45

@camerondurham

Description

@camerondurham

Details

Create unit tests to verify CLI functionality for command handlers. If possible, add integration tests to test against CLI binary.

Motivation

The purpose of doing this is to verify CLI functionality and make sure this handles all the error/success cases we think it does. Unit tests like this can speed up development and iteration because we don't have to manually verify the CLI works like we want it to, all we have to do is run go test ./....

Part of this involves making the code more test-able by breaking up commands into smaller functions that we can easily verify. This also means clearly identifying and separating dependencies to allow easier mocking so when writing a test for the functions in cli/runner/run.go we are only testing the code in that file, not also testing the code in cli/runner/client/client.go.

Possible Implementation Steps

1. Make Requester Mocks

Make a directory such as cli/runner/client/mocks to contain mocks for the Requester interface. Then you can generate mocks of the Requester interface by using the mockgen CLI. See an example from the Makefile: https://github.com/camerondurham/runner/blob/2d02bbcc19294dead678d9aa7b1ed6c885018d2c/Makefile#L80

Now you can create a requester and mock its behavior when testing the CLI.

2. Refactor CLI functions so the Requester is passed into function

This step is needed so we can make the CLI functions use the mocked requester created in 1 instead of making a "real" one.

To do this, we can refactor the CLI into the CliClient by making a struct with the CLI's dependencies. An example of this structure is the runner server Client struct: https://github.com/camerondurham/runner/blob/2d02bbcc19294dead678d9aa7b1ed6c885018d2c/cli/runner/client/client.go#L28-L31

What this allows is the ability to inject the Runner Server Client mocks.

Since the CLI really only needs the Client, the CliClient can be something like this:

type CliClient struct {
    client Requester
}

Then we can separate the runner CLI specific logic into refactor the the CLI logic into pointer receivers:

func (cli *CliClient) run(lang string, filename string) (*api.RunResponse, error) {
   // check language and load file 
   ...
   return cli.client.Run(&RunRequest{...})
    
}

3. Implement the unit tests

Now you can create unit tests that will test the CliClient structs created in 2. It will probably look something like this

cli := &CliClient{
   client: mocks.NewMockRequester()
}

See this snippet for how to make specific responses for the generated mocks:

https://github.com/camerondurham/runner/blob/2d02bbcc19294dead678d9aa7b1ed6c885018d2c/engine/coderunner/runner_test.go#L20-L33

Subtasks

Metadata

Metadata

Assignees

Labels

cliCommand Line Interfacenice to haveNot a priority

Projects

Status

Todo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions