Skip to content

Commit

Permalink
Add ability to create a clio.Application that runs test assertions in…
Browse files Browse the repository at this point in the history
…stead of cmd.RunE (#37)

In order to test the configuration setup (arg, config file, and env var parsing) in
clio applications, add a cliotestutils package that spies on the config that would be
created before calling the command's RunE instead of just wrapping it as a normal
clio.Application does. This enables consumers of the library to write unit tests that, for example, assert
that a given command accepts a given flag.

---------

Signed-off-by: Alex Goodman <[email protected]>
Signed-off-by: Will Murphy <[email protected]>
Co-authored-by: Alex Goodman <[email protected]>
  • Loading branch information
willmurphyscode and wagoodman authored Jan 31, 2024
1 parent a5e93b6 commit 9eba612
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 0 deletions.
73 changes: 73 additions & 0 deletions cliotestutils/application.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package cliotestutils

import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"

"github.com/anchore/clio"
)

// NewApplication takes a testing.T, a clio setup config, and a slice of assertions, and returns
// a clio application that will, instead of setting up commands with their normal RunE, set up commands
// such that the assertions are called with the testing.T after config state is set up by reading flags,
// env vars, and config files. Useful for testing that expected configuration options are wired up.
// Note that initializers will be cleared from the clio setup config, since the initialization may happen
// more than once and affect global state. For necessary global state, a workaround is to set it in a TestingMain.
func NewApplication(t *testing.T, cfg *clio.SetupConfig, assertions ...AssertionFunc) clio.Application {
cfg.Initializers = nil
a := clio.New(*cfg)

var asserter assertionClosure = func(cmd *cobra.Command, args []string, cfgs ...any) {
for _, assertion := range assertions {
assertion(t, cmd, args, cfgs...)
}
}

return &testApplication{
a,
asserter,
}
}

type AssertionFunc func(t *testing.T, cmd *cobra.Command, args []string, cfgs ...any)

func OptionsEquals(wantOpts any) AssertionFunc {
return func(t *testing.T, cmd *cobra.Command, args []string, cfgs ...any) {
assert.Equal(t, len(cfgs), 1)
if d := cmp.Diff(wantOpts, cfgs[0]); d != "" {
t.Errorf("mismatched options (-want +got):\n%s", d)
}
}
}

type assertionClosure func(cmd *cobra.Command, args []string, cfgs ...any)

type testApplication struct {
clio.Application
assertion assertionClosure
}

func (a *testApplication) SetupCommand(cmd *cobra.Command, cfgs ...any) *cobra.Command {
cmd.RunE = func(cmd *cobra.Command, args []string) error {
a.assertion(cmd, args, cfgs...)
return nil
}
return a.Application.SetupCommand(cmd, cfgs...)
}

func (a *testApplication) SetupRootCommand(cmd *cobra.Command, cfgs ...any) *cobra.Command {
cmd.RunE = func(cmd *cobra.Command, args []string) error {
a.assertion(cmd, args, cfgs...)
return nil
}
return a.Application.SetupRootCommand(cmd, cfgs...)
}

/*
// TODO: WISHLIST:
1. Helper to wire up a test fixture as the only config file that will be found
2. Set env vars by passing map[string]string (currently possible by caller in test; a helper here would be nice.)
*/
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.18
require (
github.com/anchore/fangs v0.0.0-20230807173929-13c94c86f47e
github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a
github.com/google/go-cmp v0.5.9
github.com/gookit/color v1.5.4
github.com/iancoleman/strcase v0.3.0
github.com/pborman/indent v1.2.1
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
Expand Down

0 comments on commit 9eba612

Please sign in to comment.