forked from exercism/cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcmd_test.go
More file actions
121 lines (106 loc) · 3.47 KB
/
cmd_test.go
File metadata and controls
121 lines (106 loc) · 3.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package cmd
import (
"io"
"os"
"testing"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
)
const cfgHomeKey = "EXERCISM_CONFIG_HOME"
// CommandTest makes it easier to write tests for Cobra commands.
//
// To initialize, give it the three fields Cmd, InitFn, and Args.
// Then call Setup, and defer the Teardown.
// The args are the faked out os.Args. The first two arguments
// in the Args will be ignored. These represent the command (e.g. exercism)
// and the subcommand (e.g. download).
// Pass any interactive responses needed for the test in a single
// String in MockInput, delimited by newlines.
//
// Finally, when you have done whatever other setup you need in your
// test, call the command by calling Execute on the App.
//
// Example:
//
// cmdTest := &CommandTest{
// Cmd: myCmd,
// InitFn: initMyCmd,
// Args: []string{"fakeapp", "mycommand", "arg1", "--flag", "value"},
// MockInteractiveResponse: "first-input\nsecond\n",
// }
//
// cmdTest.Setup(t)
// defer cmdTest.Teardown(t)
// ...
// cmdTest.App.Execute()
type CommandTest struct {
App *cobra.Command
Cmd *cobra.Command
InitFn func()
TmpDir string
Args []string
MockInteractiveResponse string
OriginalValues struct {
ConfigHome string
Args []string
}
}
// Setup does all the prep and initialization for testing a command.
// It creates a fake Cobra app to provide a clean harness for the test,
// and adds the command under test to it as a subcommand.
// It also resets and reconfigures the command under test to
// make sure we're not getting any accidental pollution from the existing
// environment or other tests. Lastly, because we need to override some of
// the global environment settings, the setup method also stores the existing
// values so that Teardown can set them back the way they were when the test
// has completed.
// The method takes a *testing.T as an argument, that way the method can
// fail the test if the creation of the temporary directory fails.
func (test *CommandTest) Setup(t *testing.T) {
dir, err := os.MkdirTemp("", "command-test")
defer os.RemoveAll(dir)
assert.NoError(t, err)
test.TmpDir = dir
test.OriginalValues.ConfigHome = os.Getenv(cfgHomeKey)
test.OriginalValues.Args = os.Args
os.Setenv(cfgHomeKey, test.TmpDir)
os.Args = test.Args
test.Cmd.ResetFlags()
test.InitFn()
test.App = &cobra.Command{}
test.App.AddCommand(test.Cmd)
test.App.SetOutput(Err)
}
// Teardown puts the environment back the way it was before the test.
// The method takes a *testing.T so that it can blow up if it fails to
// clean up after itself.
func (test *CommandTest) Teardown(t *testing.T) {
os.Setenv(cfgHomeKey, test.OriginalValues.ConfigHome)
os.Args = test.OriginalValues.Args
if err := os.RemoveAll(test.TmpDir); err != nil {
t.Fatal(err)
}
}
// capturedOutput lets us more easily redirect streams in the tests.
type capturedOutput struct {
oldOut, oldErr, newOut, newErr io.Writer
}
// newCapturedOutput creates a new value to override the streams.
func newCapturedOutput() capturedOutput {
return capturedOutput{
oldOut: Out,
oldErr: Err,
newOut: io.Discard,
newErr: io.Discard,
}
}
// override sets the package variables to the fake streams.
func (co capturedOutput) override() {
Out = co.newOut
Err = co.newErr
}
// reset puts back the original streams for the commands to write to.
func (co capturedOutput) reset() {
Out = co.oldOut
Err = co.oldErr
}