You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by lb...@apache.org on 2019/12/18 10:30:04 UTC

[camel-k] 04/10: Refactoring to allow testability of cobra/viper commands and commands flags loading.

This is an automated email from the ASF dual-hosted git repository.

lburgazzoli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k.git

commit acbb0bc6b756b736e964ce76854e83ff397cf3b8
Author: Andrea Tarocchi <an...@gmail.com>
AuthorDate: Thu Dec 12 23:32:25 2019 +0100

    Refactoring to allow testability of cobra/viper commands and commands flags loading.
---
 pkg/cmd/builder.go                   | 10 +++---
 pkg/cmd/delete.go                    | 12 +++----
 pkg/cmd/describe.go                  |  6 ++--
 pkg/cmd/describe_integration.go      | 22 ++++++-------
 pkg/cmd/describe_kit.go              | 20 +++++------
 pkg/cmd/describe_platform.go         | 20 +++++------
 pkg/cmd/get.go                       |  4 +--
 pkg/cmd/install.go                   | 12 +++----
 pkg/cmd/kit.go                       |  6 ++--
 pkg/cmd/kit_create.go                | 20 +++++------
 pkg/cmd/kit_delete.go                | 20 +++++------
 pkg/cmd/kit_get.go                   | 18 +++++-----
 pkg/cmd/log.go                       |  4 +--
 pkg/cmd/rebuild.go                   |  4 +--
 pkg/cmd/reset.go                     |  4 +--
 pkg/cmd/root.go                      | 45 +++++++++++++++++--------
 pkg/cmd/root_test.go                 | 64 ++++++++++++++++++++++++++++++++++--
 pkg/cmd/run.go                       |  4 +--
 pkg/cmd/{kit.go => run_test.go}      | 20 +++++------
 pkg/cmd/util.go                      |  4 +++
 pkg/{cmd/kit.go => util/test/cmd.go} | 31 ++++++++++-------
 21 files changed, 220 insertions(+), 130 deletions(-)

diff --git a/pkg/cmd/builder.go b/pkg/cmd/builder.go
index 9517e7d..5c541f7 100644
--- a/pkg/cmd/builder.go
+++ b/pkg/cmd/builder.go
@@ -22,8 +22,8 @@ import (
 	"github.com/spf13/cobra"
 )
 
-func newCmdBuilder(rootCmdOptions *RootCmdOptions) *cobra.Command {
-	impl := builderCmdOptions{
+func newCmdBuilder(rootCmdOptions *RootCmdOptions) (*cobra.Command, *builderCmdOptions) {
+	options := builderCmdOptions{
 		RootCmdOptions: rootCmdOptions,
 	}
 	cmd := cobra.Command{
@@ -31,13 +31,13 @@ func newCmdBuilder(rootCmdOptions *RootCmdOptions) *cobra.Command {
 		Short:   "Run the Camel K builder",
 		Long:    `Run the Camel K builder`,
 		Hidden:  true,
-		PreRunE: decode(&impl),
-		Run:     impl.run,
+		PreRunE: decode(&options),
+		Run:     options.run,
 	}
 
 	cmd.Flags().String("build-name", "", "The name of the build resource")
 
-	return &cmd
+	return &cmd, &options
 }
 
 type builderCmdOptions struct {
diff --git a/pkg/cmd/delete.go b/pkg/cmd/delete.go
index a3d9a55..aaf9863 100644
--- a/pkg/cmd/delete.go
+++ b/pkg/cmd/delete.go
@@ -31,19 +31,19 @@ import (
 )
 
 // newCmdDelete --
-func newCmdDelete(rootCmdOptions *RootCmdOptions) *cobra.Command {
-	impl := deleteCmdOptions{
+func newCmdDelete(rootCmdOptions *RootCmdOptions) (*cobra.Command, *deleteCmdOptions) {
+	options := deleteCmdOptions{
 		RootCmdOptions: rootCmdOptions,
 	}
 	cmd := cobra.Command{
 		Use:     "delete [integration1] [integration2] ...",
 		Short:   "Delete integrations deployed on Kubernetes",
-		PreRunE: decode(&impl),
+		PreRunE: decode(&options),
 		RunE: func(_ *cobra.Command, args []string) error {
-			if err := impl.validate(args); err != nil {
+			if err := options.validate(args); err != nil {
 				return err
 			}
-			if err := impl.run(args); err != nil {
+			if err := options.run(args); err != nil {
 				fmt.Println(err.Error())
 			}
 
@@ -53,7 +53,7 @@ func newCmdDelete(rootCmdOptions *RootCmdOptions) *cobra.Command {
 
 	cmd.Flags().Bool("all", false, "Delete all integrations")
 
-	return &cmd
+	return &cmd, &options
 }
 
 type deleteCmdOptions struct {
diff --git a/pkg/cmd/describe.go b/pkg/cmd/describe.go
index aa0ae52..13ed541 100644
--- a/pkg/cmd/describe.go
+++ b/pkg/cmd/describe.go
@@ -71,9 +71,9 @@ func newCmdDescribe(rootCmdOptions *RootCmdOptions) *cobra.Command {
 		Long:  `Describe a Camel K resource.`,
 	}
 
-	cmd.AddCommand(newDescribeKitCmd(rootCmdOptions))
-	cmd.AddCommand(newDescribeIntegrationCmd(rootCmdOptions))
-	cmd.AddCommand(newDescribePlatformCmd(rootCmdOptions))
+	cmd.AddCommand(cmdOnly(newDescribeKitCmd(rootCmdOptions)))
+	cmd.AddCommand(cmdOnly(newDescribeIntegrationCmd(rootCmdOptions)))
+	cmd.AddCommand(cmdOnly(newDescribePlatformCmd(rootCmdOptions)))
 
 	return &cmd
 }
diff --git a/pkg/cmd/describe_integration.go b/pkg/cmd/describe_integration.go
index 25882f4..bd4647c 100644
--- a/pkg/cmd/describe_integration.go
+++ b/pkg/cmd/describe_integration.go
@@ -30,9 +30,9 @@ import (
 	k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
-func newDescribeIntegrationCmd(rootCmdOptions *RootCmdOptions) *cobra.Command {
+func newDescribeIntegrationCmd(rootCmdOptions *RootCmdOptions) (*cobra.Command, *describeIntegrationCommandOptions) {
 
-	impl := &describeIntegrationCommand{
+	options := describeIntegrationCommandOptions{
 		RootCmdOptions: rootCmdOptions,
 	}
 
@@ -41,12 +41,12 @@ func newDescribeIntegrationCmd(rootCmdOptions *RootCmdOptions) *cobra.Command {
 		Aliases: []string{"it"},
 		Short:   "Describe an Integration",
 		Long:    `Describe an Integration.`,
-		PreRunE: decode(&impl),
+		PreRunE: decode(&options),
 		RunE: func(_ *cobra.Command, args []string) error {
-			if err := impl.validate(args); err != nil {
+			if err := options.validate(args); err != nil {
 				return err
 			}
-			if err := impl.run(args); err != nil {
+			if err := options.run(args); err != nil {
 				fmt.Println(err.Error())
 			}
 
@@ -54,24 +54,24 @@ func newDescribeIntegrationCmd(rootCmdOptions *RootCmdOptions) *cobra.Command {
 		},
 	}
 
-	cmd.Flags().BoolVar(&impl.showSourceContent, "show-source-content", false, "Print source content")
+	cmd.Flags().BoolVar(&options.showSourceContent, "show-source-content", false, "Print source content")
 
-	return &cmd
+	return &cmd, &options
 }
 
-type describeIntegrationCommand struct {
+type describeIntegrationCommandOptions struct {
 	*RootCmdOptions
 	showSourceContent bool `mapstructure:"show-source-content"`
 }
 
-func (command *describeIntegrationCommand) validate(args []string) error {
+func (command *describeIntegrationCommandOptions) validate(args []string) error {
 	if len(args) != 1 {
 		return fmt.Errorf("accepts at least 1 arg, received %d", len(args))
 	}
 	return nil
 }
 
-func (command *describeIntegrationCommand) run(args []string) error {
+func (command *describeIntegrationCommandOptions) run(args []string) error {
 	c, err := command.GetCmdClient()
 	if err != nil {
 		return err
@@ -92,7 +92,7 @@ func (command *describeIntegrationCommand) run(args []string) error {
 	return nil
 }
 
-func (command *describeIntegrationCommand) describeIntegration(i v1alpha1.Integration) string {
+func (command *describeIntegrationCommandOptions) describeIntegration(i v1alpha1.Integration) string {
 	return indentedwriter.IndentedString(func(out io.Writer) {
 		w := indentedwriter.NewWriter(out)
 
diff --git a/pkg/cmd/describe_kit.go b/pkg/cmd/describe_kit.go
index a3ce855..c0d42f6 100644
--- a/pkg/cmd/describe_kit.go
+++ b/pkg/cmd/describe_kit.go
@@ -28,8 +28,8 @@ import (
 	k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
-func newDescribeKitCmd(rootCmdOptions *RootCmdOptions) *cobra.Command {
-	impl := &describeKitCommand{
+func newDescribeKitCmd(rootCmdOptions *RootCmdOptions) (*cobra.Command, *describeKitCommandOptions) {
+	options := describeKitCommandOptions{
 		rootCmdOptions,
 	}
 
@@ -37,12 +37,12 @@ func newDescribeKitCmd(rootCmdOptions *RootCmdOptions) *cobra.Command {
 		Use:     "kit",
 		Short:   "Describe an Integration Kit",
 		Long:    `Describe an Integration Kit.`,
-		PreRunE: decode(impl),
+		PreRunE: decode(options),
 		RunE: func(_ *cobra.Command, args []string) error {
-			if err := impl.validate(args); err != nil {
+			if err := options.validate(args); err != nil {
 				return err
 			}
-			if err := impl.run(args); err != nil {
+			if err := options.run(args); err != nil {
 				fmt.Println(err.Error())
 			}
 
@@ -50,21 +50,21 @@ func newDescribeKitCmd(rootCmdOptions *RootCmdOptions) *cobra.Command {
 		},
 	}
 
-	return &cmd
+	return &cmd, &options
 }
 
-type describeKitCommand struct {
+type describeKitCommandOptions struct {
 	*RootCmdOptions
 }
 
-func (command *describeKitCommand) validate(args []string) error {
+func (command *describeKitCommandOptions) validate(args []string) error {
 	if len(args) != 1 {
 		return fmt.Errorf("accepts at least 1 arg, received %d", len(args))
 	}
 	return nil
 }
 
-func (command *describeKitCommand) run(args []string) error {
+func (command *describeKitCommandOptions) run(args []string) error {
 	c, err := command.GetCmdClient()
 	if err != nil {
 		return err
@@ -85,7 +85,7 @@ func (command *describeKitCommand) run(args []string) error {
 	return nil
 }
 
-func (command *describeKitCommand) describeIntegrationKit(kit v1alpha1.IntegrationKit) string {
+func (command *describeKitCommandOptions) describeIntegrationKit(kit v1alpha1.IntegrationKit) string {
 	return indentedwriter.IndentedString(func(out io.Writer) {
 		w := indentedwriter.NewWriter(out)
 
diff --git a/pkg/cmd/describe_platform.go b/pkg/cmd/describe_platform.go
index 17c639b..ddcb799 100644
--- a/pkg/cmd/describe_platform.go
+++ b/pkg/cmd/describe_platform.go
@@ -28,8 +28,8 @@ import (
 	k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
-func newDescribePlatformCmd(rootCmdOptions *RootCmdOptions) *cobra.Command {
-	impl := &describePlatformCommand{
+func newDescribePlatformCmd(rootCmdOptions *RootCmdOptions) (*cobra.Command, *describePlatformCommandOptions) {
+	options := describePlatformCommandOptions{
 		rootCmdOptions,
 	}
 
@@ -37,12 +37,12 @@ func newDescribePlatformCmd(rootCmdOptions *RootCmdOptions) *cobra.Command {
 		Use:     "platform",
 		Short:   "Describe an Integration Platform",
 		Long:    `Describe an Integration Platform.`,
-		PreRunE: decode(impl),
+		PreRunE: decode(options),
 		RunE: func(_ *cobra.Command, args []string) error {
-			if err := impl.validate(args); err != nil {
+			if err := options.validate(args); err != nil {
 				return err
 			}
-			if err := impl.run(args); err != nil {
+			if err := options.run(args); err != nil {
 				fmt.Println(err.Error())
 			}
 
@@ -50,21 +50,21 @@ func newDescribePlatformCmd(rootCmdOptions *RootCmdOptions) *cobra.Command {
 		},
 	}
 
-	return &cmd
+	return &cmd, &options
 }
 
-type describePlatformCommand struct {
+type describePlatformCommandOptions struct {
 	*RootCmdOptions
 }
 
-func (command *describePlatformCommand) validate(args []string) error {
+func (command *describePlatformCommandOptions) validate(args []string) error {
 	if len(args) != 1 {
 		return fmt.Errorf("accepts at least 1 arg, received %d", len(args))
 	}
 	return nil
 }
 
-func (command *describePlatformCommand) run(args []string) error {
+func (command *describePlatformCommandOptions) run(args []string) error {
 	c, err := command.GetCmdClient()
 	if err != nil {
 		return err
@@ -85,7 +85,7 @@ func (command *describePlatformCommand) run(args []string) error {
 	return nil
 }
 
-func (command *describePlatformCommand) describeIntegrationPlatform(platform v1alpha1.IntegrationPlatform) string {
+func (command *describePlatformCommandOptions) describeIntegrationPlatform(platform v1alpha1.IntegrationPlatform) string {
 	return indentedwriter.IndentedString(func(out io.Writer) {
 		w := indentedwriter.NewWriter(out)
 		describeObjectMeta(w, platform.ObjectMeta)
diff --git a/pkg/cmd/get.go b/pkg/cmd/get.go
index d5dea19..3e10f96 100644
--- a/pkg/cmd/get.go
+++ b/pkg/cmd/get.go
@@ -34,7 +34,7 @@ type getCmdOptions struct {
 	*RootCmdOptions
 }
 
-func newCmdGet(rootCmdOptions *RootCmdOptions) *cobra.Command {
+func newCmdGet(rootCmdOptions *RootCmdOptions) (*cobra.Command, *getCmdOptions) {
 	options := getCmdOptions{
 		RootCmdOptions: rootCmdOptions,
 	}
@@ -46,7 +46,7 @@ func newCmdGet(rootCmdOptions *RootCmdOptions) *cobra.Command {
 		RunE:    options.run,
 	}
 
-	return &cmd
+	return &cmd, &options
 }
 
 func (o *getCmdOptions) run(cmd *cobra.Command, args []string) error {
diff --git a/pkg/cmd/install.go b/pkg/cmd/install.go
index bc10726..96289da 100644
--- a/pkg/cmd/install.go
+++ b/pkg/cmd/install.go
@@ -44,20 +44,20 @@ import (
 	k8serrors "k8s.io/apimachinery/pkg/api/errors"
 )
 
-func newCmdInstall(rootCmdOptions *RootCmdOptions) *cobra.Command {
-	impl := installCmdOptions{
+func newCmdInstall(rootCmdOptions *RootCmdOptions) (*cobra.Command, *installCmdOptions) {
+	options := installCmdOptions{
 		RootCmdOptions: rootCmdOptions,
 	}
 	cmd := cobra.Command{
 		Use:     "install",
 		Short:   "Installs Camel K on a Kubernetes cluster",
 		Long:    `Installs Camel K on a Kubernetes or OpenShift cluster.`,
-		PreRunE: impl.decode,
+		PreRunE: options.decode,
 		RunE: func(cmd *cobra.Command, args []string) error {
-			if err := impl.validate(cmd, args); err != nil {
+			if err := options.validate(cmd, args); err != nil {
 				return err
 			}
-			if err := impl.install(cmd, args); err != nil {
+			if err := options.install(cmd, args); err != nil {
 				return err
 			}
 			return nil
@@ -107,7 +107,7 @@ func newCmdInstall(rootCmdOptions *RootCmdOptions) *cobra.Command {
 		},
 	)
 
-	return &cmd
+	return &cmd, &options
 }
 
 type installCmdOptions struct {
diff --git a/pkg/cmd/kit.go b/pkg/cmd/kit.go
index 67d7ef1..726f09d 100644
--- a/pkg/cmd/kit.go
+++ b/pkg/cmd/kit.go
@@ -28,9 +28,9 @@ func newCmdKit(rootCmdOptions *RootCmdOptions) *cobra.Command {
 		Long:  `Configure an Integration Kit.`,
 	}
 
-	cmd.AddCommand(newKitCreateCmd(rootCmdOptions))
-	cmd.AddCommand(newKitDeleteCmd(rootCmdOptions))
-	cmd.AddCommand(newKitGetCmd(rootCmdOptions))
+	cmd.AddCommand(cmdOnly(newKitCreateCmd(rootCmdOptions)))
+	cmd.AddCommand(cmdOnly(newKitDeleteCmd(rootCmdOptions)))
+	cmd.AddCommand(cmdOnly(newKitGetCmd(rootCmdOptions)))
 
 	return &cmd
 }
diff --git a/pkg/cmd/kit_create.go b/pkg/cmd/kit_create.go
index 3f66c2f..a29c4b3 100644
--- a/pkg/cmd/kit_create.go
+++ b/pkg/cmd/kit_create.go
@@ -33,8 +33,8 @@ import (
 	k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
-func newKitCreateCmd(rootCmdOptions *RootCmdOptions) *cobra.Command {
-	impl := &kitCreateCommand{
+func newKitCreateCmd(rootCmdOptions *RootCmdOptions) (*cobra.Command, *kitCreateCommandOptions) {
+	options := kitCreateCommandOptions{
 		RootCmdOptions: rootCmdOptions,
 	}
 
@@ -42,9 +42,9 @@ func newKitCreateCmd(rootCmdOptions *RootCmdOptions) *cobra.Command {
 		Use:     "create <name>",
 		Short:   "Create an Integration Kit",
 		Long:    `Create an Integration Kit.`,
-		Args:    impl.validateArgs,
-		PreRunE: decode(impl),
-		RunE:    impl.run,
+		Args:    options.validateArgs,
+		PreRunE: decode(options),
+		RunE:    options.run,
 	}
 
 	cmd.Flags().String("image", "", "Image used to create the kit")
@@ -58,10 +58,10 @@ func newKitCreateCmd(rootCmdOptions *RootCmdOptions) *cobra.Command {
 	// completion support
 	configureKnownCompletions(&cmd)
 
-	return &cmd
+	return &cmd, &options
 }
 
-type kitCreateCommand struct {
+type kitCreateCommandOptions struct {
 	*RootCmdOptions
 
 	Image        string   `mapstructure:"image"`
@@ -73,7 +73,7 @@ type kitCreateCommand struct {
 	Traits       []string `mapstructure:"traits"`
 }
 
-func (command *kitCreateCommand) validateArgs(_ *cobra.Command, args []string) error {
+func (command *kitCreateCommandOptions) validateArgs(_ *cobra.Command, args []string) error {
 	if len(args) != 1 {
 		return errors.New("create expects a single name argument")
 	}
@@ -81,7 +81,7 @@ func (command *kitCreateCommand) validateArgs(_ *cobra.Command, args []string) e
 	return nil
 }
 
-func (command *kitCreateCommand) run(_ *cobra.Command, args []string) error {
+func (command *kitCreateCommandOptions) run(_ *cobra.Command, args []string) error {
 	c, err := command.GetCmdClient()
 	if err != nil {
 		return err
@@ -201,7 +201,7 @@ func (command *kitCreateCommand) run(_ *cobra.Command, args []string) error {
 	return nil
 }
 
-func (*kitCreateCommand) configureTrait(ctx *v1alpha1.IntegrationKit, config string) error {
+func (*kitCreateCommandOptions) configureTrait(ctx *v1alpha1.IntegrationKit, config string) error {
 	if ctx.Spec.Traits == nil {
 		ctx.Spec.Traits = make(map[string]v1alpha1.TraitSpec)
 	}
diff --git a/pkg/cmd/kit_delete.go b/pkg/cmd/kit_delete.go
index 7902fae..3d02d03 100644
--- a/pkg/cmd/kit_delete.go
+++ b/pkg/cmd/kit_delete.go
@@ -29,8 +29,8 @@ import (
 	k8errors "k8s.io/apimachinery/pkg/api/errors"
 )
 
-func newKitDeleteCmd(rootCmdOptions *RootCmdOptions) *cobra.Command {
-	impl := kitDeleteCommand{
+func newKitDeleteCmd(rootCmdOptions *RootCmdOptions) (*cobra.Command, *kitDeleteCommandOptions) {
+	options := kitDeleteCommandOptions{
 		RootCmdOptions: rootCmdOptions,
 	}
 
@@ -38,12 +38,12 @@ func newKitDeleteCmd(rootCmdOptions *RootCmdOptions) *cobra.Command {
 		Use:     "delete",
 		Short:   "Delete an Integration Kit",
 		Long:    `Delete an Integration Kit.`,
-		PreRunE: decode(&impl),
+		PreRunE: decode(&options),
 		RunE: func(_ *cobra.Command, args []string) error {
-			if err := impl.validate(args); err != nil {
+			if err := options.validate(args); err != nil {
 				return err
 			}
-			if err := impl.run(args); err != nil {
+			if err := options.run(args); err != nil {
 				fmt.Println(err.Error())
 			}
 
@@ -53,15 +53,15 @@ func newKitDeleteCmd(rootCmdOptions *RootCmdOptions) *cobra.Command {
 
 	cmd.Flags().Bool("all", false, "Delete all integration Kits")
 
-	return &cmd
+	return &cmd, &options
 }
 
-type kitDeleteCommand struct {
+type kitDeleteCommandOptions struct {
 	*RootCmdOptions
 	All bool `mapstructure:"all"`
 }
 
-func (command *kitDeleteCommand) validate(args []string) error {
+func (command *kitDeleteCommandOptions) validate(args []string) error {
 	if command.All && len(args) > 0 {
 		return errors.New("invalid combination: both all flag and named Kits are set")
 	}
@@ -72,7 +72,7 @@ func (command *kitDeleteCommand) validate(args []string) error {
 	return nil
 }
 
-func (command *kitDeleteCommand) run(args []string) error {
+func (command *kitDeleteCommandOptions) run(args []string) error {
 	names := args
 
 	c, err := command.GetCmdClient()
@@ -104,7 +104,7 @@ func (command *kitDeleteCommand) run(args []string) error {
 	return nil
 }
 
-func (command *kitDeleteCommand) delete(name string) error {
+func (command *kitDeleteCommandOptions) delete(name string) error {
 	ctx := v1alpha1.NewIntegrationKit(command.Namespace, name)
 	key := k8sclient.ObjectKey{
 		Namespace: command.Namespace,
diff --git a/pkg/cmd/kit_get.go b/pkg/cmd/kit_get.go
index a22af7b..b20489f 100644
--- a/pkg/cmd/kit_get.go
+++ b/pkg/cmd/kit_get.go
@@ -28,8 +28,8 @@ import (
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 )
 
-func newKitGetCmd(rootCmdOptions *RootCmdOptions) *cobra.Command {
-	impl := kitGetCommand{
+func newKitGetCmd(rootCmdOptions *RootCmdOptions) (*cobra.Command, *kitGetCommandOptions) {
+	options := kitGetCommandOptions{
 		RootCmdOptions: rootCmdOptions,
 	}
 
@@ -37,12 +37,12 @@ func newKitGetCmd(rootCmdOptions *RootCmdOptions) *cobra.Command {
 		Use:     "get",
 		Short:   "Get defined Integration Kit",
 		Long:    `Get defined Integration Kit.`,
-		PreRunE: decode(&impl),
+		PreRunE: decode(&options),
 		RunE: func(cmd *cobra.Command, args []string) error {
-			if err := impl.validate(cmd, args); err != nil {
+			if err := options.validate(cmd, args); err != nil {
 				return err
 			}
-			if err := impl.run(cmd); err != nil {
+			if err := options.run(cmd); err != nil {
 				fmt.Println(err.Error())
 			}
 
@@ -54,21 +54,21 @@ func newKitGetCmd(rootCmdOptions *RootCmdOptions) *cobra.Command {
 	cmd.Flags().Bool(v1alpha1.IntegrationKitTypeExternal, true, "Includes external Kits")
 	cmd.Flags().Bool(v1alpha1.IntegrationKitTypePlatform, true, "Includes platform Kits")
 
-	return &cmd
+	return &cmd, &options
 }
 
-type kitGetCommand struct {
+type kitGetCommandOptions struct {
 	*RootCmdOptions
 	User     bool `mapstructure:"user"`
 	External bool `mapstructure:"external"`
 	Platform bool `mapstructure:"platform"`
 }
 
-func (command *kitGetCommand) validate(cmd *cobra.Command, args []string) error {
+func (command *kitGetCommandOptions) validate(cmd *cobra.Command, args []string) error {
 	return nil
 }
 
-func (command *kitGetCommand) run(cmd *cobra.Command) error {
+func (command *kitGetCommandOptions) run(cmd *cobra.Command) error {
 	kitList := v1alpha1.NewIntegrationKitList()
 	c, err := command.GetCmdClient()
 	if err != nil {
diff --git a/pkg/cmd/log.go b/pkg/cmd/log.go
index 852c9c3..de6d0db 100644
--- a/pkg/cmd/log.go
+++ b/pkg/cmd/log.go
@@ -27,7 +27,7 @@ import (
 	k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
-func newCmdLog(rootCmdOptions *RootCmdOptions) *cobra.Command {
+func newCmdLog(rootCmdOptions *RootCmdOptions) (*cobra.Command, *logCmdOptions) {
 	options := logCmdOptions{
 		RootCmdOptions: rootCmdOptions,
 	}
@@ -44,7 +44,7 @@ func newCmdLog(rootCmdOptions *RootCmdOptions) *cobra.Command {
 	// completion support
 	configureKnownCompletions(&cmd)
 
-	return &cmd
+	return &cmd, &options
 }
 
 type logCmdOptions struct {
diff --git a/pkg/cmd/rebuild.go b/pkg/cmd/rebuild.go
index d88738a..82e845c 100644
--- a/pkg/cmd/rebuild.go
+++ b/pkg/cmd/rebuild.go
@@ -29,7 +29,7 @@ import (
 	"github.com/apache/camel-k/pkg/client"
 )
 
-func newCmdRebuild(rootCmdOptions *RootCmdOptions) *cobra.Command {
+func newCmdRebuild(rootCmdOptions *RootCmdOptions) (*cobra.Command, *rebuildCmdOptions) {
 	options := rebuildCmdOptions{
 		RootCmdOptions: rootCmdOptions,
 	}
@@ -41,7 +41,7 @@ func newCmdRebuild(rootCmdOptions *RootCmdOptions) *cobra.Command {
 		RunE:    options.rebuild,
 	}
 
-	return &cmd
+	return &cmd, &options
 }
 
 type rebuildCmdOptions struct {
diff --git a/pkg/cmd/reset.go b/pkg/cmd/reset.go
index 02bd058..0275493 100644
--- a/pkg/cmd/reset.go
+++ b/pkg/cmd/reset.go
@@ -29,7 +29,7 @@ import (
 	"github.com/apache/camel-k/pkg/client"
 )
 
-func newCmdReset(rootCmdOptions *RootCmdOptions) *cobra.Command {
+func newCmdReset(rootCmdOptions *RootCmdOptions) (*cobra.Command, *resetCmdOptions) {
 	options := resetCmdOptions{
 		RootCmdOptions: rootCmdOptions,
 	}
@@ -44,7 +44,7 @@ func newCmdReset(rootCmdOptions *RootCmdOptions) *cobra.Command {
 	cmd.Flags().Bool("skip-kits", false, "Do not delete the integration kits")
 	cmd.Flags().Bool("skip-integrations", false, "Do not delete the integrations")
 
-	return &cmd
+	return &cmd, &options
 }
 
 type resetCmdOptions struct {
diff --git a/pkg/cmd/root.go b/pkg/cmd/root.go
index c553c2a..cc8473d 100644
--- a/pkg/cmd/root.go
+++ b/pkg/cmd/root.go
@@ -46,6 +46,17 @@ func NewKamelCommand(ctx context.Context) (*cobra.Command, error) {
 	options := RootCmdOptions{
 		Context: ctx,
 	}
+
+	var err error
+	cmd := kamelPreAddCommandInit(options)
+	cmd = addKamelSubcommands(*cmd, options)
+	cmd, err = kamelPostAddCommandInit(*cmd)
+
+	return cmd, err
+}
+
+func kamelPreAddCommandInit(options RootCmdOptions) *cobra.Command {
+
 	var cmd = cobra.Command{
 		BashCompletionFunction: bashCompletionFunction,
 		PersistentPreRunE:      options.preRun,
@@ -57,20 +68,10 @@ func NewKamelCommand(ctx context.Context) (*cobra.Command, error) {
 	cmd.PersistentFlags().StringVar(&options.KubeConfig, "config", os.Getenv("KUBECONFIG"), "Path to the config file to use for CLI requests")
 	cmd.PersistentFlags().StringVarP(&options.Namespace, "namespace", "n", "", "Namespace to use for all operations")
 
-	cmd.AddCommand(newCmdCompletion(&cmd))
-	cmd.AddCommand(newCmdVersion())
-	cmd.AddCommand(newCmdRun(&options))
-	cmd.AddCommand(newCmdGet(&options))
-	cmd.AddCommand(newCmdDelete(&options))
-	cmd.AddCommand(newCmdInstall(&options))
-	cmd.AddCommand(newCmdLog(&options))
-	cmd.AddCommand(newCmdKit(&options))
-	cmd.AddCommand(newCmdReset(&options))
-	cmd.AddCommand(newCmdDescribe(&options))
-	cmd.AddCommand(newCmdRebuild(&options))
-	cmd.AddCommand(newCmdOperator())
-	cmd.AddCommand(newCmdBuilder(&options))
+	return &cmd
+}
 
+func kamelPostAddCommandInit(cmd cobra.Command) (*cobra.Command, error) {
 	if err := bindPFlagsHierarchy(&cmd); err != nil {
 		return nil, err
 	}
@@ -98,6 +99,24 @@ func NewKamelCommand(ctx context.Context) (*cobra.Command, error) {
 	return &cmd, nil
 }
 
+func addKamelSubcommands(cmd cobra.Command, options RootCmdOptions) *cobra.Command {
+	cmd.AddCommand(newCmdCompletion(&cmd))
+	cmd.AddCommand(newCmdVersion())
+	cmd.AddCommand(cmdOnly(newCmdRun(&options)))
+	cmd.AddCommand(cmdOnly(newCmdGet(&options)))
+	cmd.AddCommand(cmdOnly(newCmdDelete(&options)))
+	cmd.AddCommand(cmdOnly(newCmdInstall(&options)))
+	cmd.AddCommand(cmdOnly(newCmdLog(&options)))
+	cmd.AddCommand(newCmdKit(&options))
+	cmd.AddCommand(cmdOnly(newCmdReset(&options)))
+	cmd.AddCommand(newCmdDescribe(&options))
+	cmd.AddCommand(cmdOnly(newCmdRebuild(&options)))
+	cmd.AddCommand(newCmdOperator())
+	cmd.AddCommand(cmdOnly(newCmdBuilder(&options)))
+
+	return &cmd
+}
+
 func (command *RootCmdOptions) preRun(cmd *cobra.Command, _ []string) error {
 	if command.Namespace == "" {
 		current, err := client.GetCurrentNamespace(command.KubeConfig)
diff --git a/pkg/cmd/root_test.go b/pkg/cmd/root_test.go
index 57f6621..7505ffb 100644
--- a/pkg/cmd/root_test.go
+++ b/pkg/cmd/root_test.go
@@ -17,7 +17,67 @@ limitations under the License.
 
 package cmd
 
-import "testing"
+import (
+	"context"
+	"github.com/apache/camel-k/pkg/util/test"
+	"github.com/spf13/cobra"
+	"os"
+	"testing"
+)
 
-func TestDecodeRoot(t *testing.T) {
+func kamelTestPostAddCommandInit(rootCmd *cobra.Command) *cobra.Command {
+	rootCmd, _ = kamelPostAddCommandInit(*rootCmd)
+	return rootCmd
+}
+
+func kamelTestPreAddCommandInit() (RootCmdOptions, *cobra.Command) {
+	options := RootCmdOptions{
+		Context: context.Background(),
+	}
+	rootCmd := kamelPreAddCommandInit(options)
+	rootCmd.Run = test.EmptyRun
+	return options, rootCmd
+}
+
+func TestLoadFromCommandLine(t *testing.T) {
+	options, rootCmd := kamelTestPreAddCommandInit()
+
+	runCmdOptions := addTestRunCmd(options, rootCmd)
+
+	rootCmd = kamelTestPostAddCommandInit(rootCmd)
+
+	_, err := test.ExecuteCommand(rootCmd, "run", "route.java", "--env", "VAR1=value,othervalue", "--env", "VAR2=value2")
+	if err != nil {
+		t.Fatalf("Unexpected error: %v", err)
+	}
+
+	if len(runCmdOptions.EnvVars) != 2 {
+		t.Errorf("Properties expected to contain: \n %v elements\nGot:\n %v elemtns\n", 2, len(runCmdOptions.EnvVars))
+	}
+	if runCmdOptions.EnvVars[0] != "VAR1=value,othervalue" || runCmdOptions.EnvVars[1] != "VAR2=value2" {
+		t.Errorf("EnvVars expected to be: \n %v\nGot:\n %v\n", "[VAR1=value,othervalue VAR=value2]", runCmdOptions.EnvVars)
+	}
+}
+
+func TestLoadFromEnvVar(t *testing.T) {
+	//shows how to include a "," character inside an env value see VAR1 value
+	os.Setenv("KAMEL_RUN_ENVS", "\"VAR1=value,\"\"othervalue\"\"\",VAR2=value2")
+
+	options, rootCmd := kamelTestPreAddCommandInit()
+
+	runCmdOptions := addTestRunCmd(options, rootCmd)
+
+	rootCmd = kamelTestPostAddCommandInit(rootCmd)
+
+	_, err := test.ExecuteCommand(rootCmd, "run", "route.java")
+	if err != nil {
+		t.Fatalf("Unexpected error: %v", err)
+	}
+
+	if len(runCmdOptions.EnvVars) != 2 {
+		t.Fatalf("Properties expected to contain: \n %v elements\nGot:\n %v elemtns\n", 2, len(runCmdOptions.EnvVars))
+	}
+	if runCmdOptions.EnvVars[0] != "VAR1=value,\"othervalue\"" || runCmdOptions.EnvVars[1] != "VAR2=value2" {
+		t.Fatalf("EnvVars expected to be: \n %v\nGot:\n %v\n", "[VAR1=value,\"othervalue\" VAR=value2]", runCmdOptions.EnvVars)
+	}
 }
diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go
index 4c41081..df1b9b6 100644
--- a/pkg/cmd/run.go
+++ b/pkg/cmd/run.go
@@ -53,7 +53,7 @@ var (
 	traitConfigRegexp = regexp.MustCompile(`^([a-z-]+)((?:\.[a-z-]+)+)=(.*)$`)
 )
 
-func newCmdRun(rootCmdOptions *RootCmdOptions) *cobra.Command {
+func newCmdRun(rootCmdOptions *RootCmdOptions) (*cobra.Command, *runCmdOptions) {
 	options := runCmdOptions{
 		RootCmdOptions: rootCmdOptions,
 	}
@@ -91,7 +91,7 @@ func newCmdRun(rootCmdOptions *RootCmdOptions) *cobra.Command {
 	// completion support
 	configureKnownCompletions(&cmd)
 
-	return &cmd
+	return &cmd, &options
 }
 
 type runCmdOptions struct {
diff --git a/pkg/cmd/kit.go b/pkg/cmd/run_test.go
similarity index 68%
copy from pkg/cmd/kit.go
copy to pkg/cmd/run_test.go
index 67d7ef1..bd32ed4 100644
--- a/pkg/cmd/kit.go
+++ b/pkg/cmd/run_test.go
@@ -18,19 +18,17 @@ limitations under the License.
 package cmd
 
 import (
+	"github.com/apache/camel-k/pkg/util/test"
 	"github.com/spf13/cobra"
 )
 
-func newCmdKit(rootCmdOptions *RootCmdOptions) *cobra.Command {
-	cmd := cobra.Command{
-		Use:   "kit",
-		Short: "Configure an Integration Kit",
-		Long:  `Configure an Integration Kit.`,
+func addTestRunCmd(options RootCmdOptions, rootCmd *cobra.Command) *runCmdOptions {
+	//add a testing version of run Command
+	runCmd, runCmdOptions := newCmdRun(&options)
+	runCmd.RunE = func(c *cobra.Command, args []string) error {
+		return nil
 	}
-
-	cmd.AddCommand(newKitCreateCmd(rootCmdOptions))
-	cmd.AddCommand(newKitDeleteCmd(rootCmdOptions))
-	cmd.AddCommand(newKitGetCmd(rootCmdOptions))
-
-	return &cmd
+	runCmd.Args = test.ArbitraryArgs
+	rootCmd.AddCommand(runCmd)
+	return runCmdOptions
 }
diff --git a/pkg/cmd/util.go b/pkg/cmd/util.go
index a32dcce..8ee87ac 100644
--- a/pkg/cmd/util.go
+++ b/pkg/cmd/util.go
@@ -183,3 +183,7 @@ func stringToSliceHookFunc(comma rune) mapstructure.DecodeHookFunc {
 		return csvReader.Read()
 	}
 }
+
+func cmdOnly(cmd *cobra.Command, options interface{}) *cobra.Command {
+	return cmd
+}
diff --git a/pkg/cmd/kit.go b/pkg/util/test/cmd.go
similarity index 59%
copy from pkg/cmd/kit.go
copy to pkg/util/test/cmd.go
index 67d7ef1..4988d78 100644
--- a/pkg/cmd/kit.go
+++ b/pkg/util/test/cmd.go
@@ -15,22 +15,31 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package cmd
+package test
 
 import (
+	"bytes"
 	"github.com/spf13/cobra"
 )
 
-func newCmdKit(rootCmdOptions *RootCmdOptions) *cobra.Command {
-	cmd := cobra.Command{
-		Use:   "kit",
-		Short: "Configure an Integration Kit",
-		Long:  `Configure an Integration Kit.`,
-	}
+func EmptyRun(*cobra.Command, []string) {}
 
-	cmd.AddCommand(newKitCreateCmd(rootCmdOptions))
-	cmd.AddCommand(newKitDeleteCmd(rootCmdOptions))
-	cmd.AddCommand(newKitGetCmd(rootCmdOptions))
+func ArbitraryArgs(cmd *cobra.Command, args []string) error {
+	return nil
+}
+
+func ExecuteCommand(root *cobra.Command, args ...string) (output string, err error) {
+	_, output, err = ExecuteCommandC(root, args...)
+	return output, err
+}
+
+func ExecuteCommandC(root *cobra.Command, args ...string) (c *cobra.Command, output string, err error) {
+	buf := new(bytes.Buffer)
+	root.SetOut(buf)
+	root.SetErr(buf)
+	root.SetArgs(args)
+
+	c, err = root.ExecuteC()
 
-	return &cmd
+	return c, buf.String(), err
 }