Skip to content

Commit

Permalink
Additional test examples
Browse files Browse the repository at this point in the history
This patch restructured and introduces the kind example.
It also introduces example/lifecycle with detail about
the stages of a test and how to override them.

Signed-off-by: Vladimir Vivien <vivienv@vmware.com>
  • Loading branch information
vladimirvivien committed Sep 17, 2021
1 parent 63fa8b0 commit 074fd98
Show file tree
Hide file tree
Showing 11 changed files with 164 additions and 9 deletions.
15 changes: 15 additions & 0 deletions examples/kind/README.md
@@ -0,0 +1,15 @@
# Example with KinD Clusters

The examples in this directory shows how to use the framework to set up kind clusters that can be used to test cluster resources.

## Predefined environment functions
> See [source code](./custom_funcs)
This directory provides an example that highlights the kind predefined environment functions, that ship with the framework.
These functions can be used to in `Setup` and `Finish` steps to automatically create and teardown a kind cluster respectively.

## Custom environment functions
> See [source code](./custom_funcs)
This example shows how you can write your own custom environment functions to specify the `Setup` and `Finish`
steps that are used to create and teardown a kind cluster respectively.
File renamed without changes.
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package custom_env_funcs
package custom_funcs

import (
"context"
Expand Down
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package custom_env_funcs
package custom_funcs

import (
"context"
Expand Down
File renamed without changes.
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package predefined_env_funcs
package predefined_funcs

import (
"context"
Expand Down
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package predefined_env_funcs
package predefined_funcs

import (
"os"
Expand Down
19 changes: 19 additions & 0 deletions examples/lifecycle/README.md
@@ -0,0 +1,19 @@
# Test Feature Lifecycle

The example in this package highlights the lifecycle of a feature test as supported by the framework.
At runtime (or test time), a test goes through multiple stages as listed below:
```
- env.Setup
- env.BeforeEachTest
env.Test {
- env.BeforeEachFeature
- feature.Feature.Setup
- feature.Assessment
- feature.Teardown
- env.AfterEachFeature
}
- env.AfterEachTest
- env.Finish
```

The framework API allows test writers to override each stage by defining a function that is executed at the appropriate stage.
116 changes: 116 additions & 0 deletions examples/lifecycle/lifecycle_test.go
@@ -0,0 +1,116 @@
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package lifecycle

import (
"context"
"fmt"
"io/ioutil"
"math/rand"
"os"
"path/filepath"
"strconv"
"testing"
"time"

"sigs.k8s.io/e2e-framework/pkg/env"
"sigs.k8s.io/e2e-framework/pkg/envconf"
"sigs.k8s.io/e2e-framework/pkg/features"
)

var test = env.New()

// TestMain defined package-wide (or test suite) configuration.
func TestMain(m *testing.M){
// define a setup function
test.Setup(func(ctx context.Context, config *envconf.Config) (context.Context, error) {
fileName := filepath.Join(os.TempDir(), "randfile.txt")
return context.WithValue(ctx, "randfile", fileName), nil
})

// BeforeEachFeature specifies behavior that occurs before each feature is tested.
// Write a random number in the file.
test.BeforeEachFeature(func(ctx context.Context, config *envconf.Config) (context.Context, error) {
fileName := ctx.Value("randfile").(string) // in real world use, check type assertion
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
// save random number in file
err := ioutil.WriteFile(fileName, []byte(fmt.Sprintf("%d", rnd.Intn(255))), 0644)
return ctx, err
})

// AfterEachFeature specifies behavior that occurs after each feature is tested.
// Clear the file prior to next text (this step is illustrative)
test.AfterEachFeature(func(ctx context.Context, config *envconf.Config) (context.Context, error) {
fileName := ctx.Value("randfile").(string) // in real world use, check type assertion
err := ioutil.WriteFile(fileName, []byte(""), 0644)
return ctx, err
})

// Finish tears down resources used in the test suite
test.Finish(func(ctx context.Context, config *envconf.Config) (context.Context, error) {
fileName := ctx.Value("randfile").(string) // in real world use, check type assertion
return ctx, os.RemoveAll(fileName)
})

// Don't forget to run the package test
os.Exit(test.Run(m))
}

// TestGuessNumberFeature shows the use of before and after
func TestGuessNumberFeature(t *testing.T) {
f := features.New("guesses")
// Setup defines setup behavior executed prior to running assessment
// This can be used to prepare data for the test.
f.Setup(func(ctx context.Context, t *testing.T, config *envconf.Config) context.Context {
fileName := ctx.Value("randfile").(string) // in real world use, check type assertion
data, err := ioutil.ReadFile(fileName)
if err != nil {
t.Fatal(err)
}
val, err := strconv.Atoi(string(data))
if err != nil {
t.Fatalf("unable to read file: %s", err)
}
return context.WithValue(ctx, "number", val)
})

// Assess define a test behavior for the feature
f.Assess("gess-high", func(ctx context.Context, t *testing.T, config *envconf.Config) context.Context {
number := ctx.Value("number").(int) // in real world use, check type assertion
if number >= 200 {
t.Error("Guessed too high")
}
return ctx
})

// Assess define at test behavior
f.Assess("gess-low", func(ctx context.Context, t *testing.T, config *envconf.Config) context.Context {
number := ctx.Value("number").(int) // in real world use, check type assertion
if number <= 50 {
t.Error("Guessed too high")
}
return ctx
})

// Teardown defines behavior to clean up after the feature is tested.
f.Teardown(func(ctx context.Context, t *testing.T, config *envconf.Config) context.Context {
return context.WithValue(ctx, "number", -1)
})

// Test the feature
test.Test(t, f.Feature())
}
13 changes: 9 additions & 4 deletions pkg/envconf/config.go
Expand Up @@ -77,11 +77,16 @@ func NewFromFlags() (*Config, error) {
if kubecfg == "" {
kubecfg = conf.ResolveKubeConfigFile()
}
c, err := klient.NewWithKubeConfigFile(kubecfg)
if err != nil {
return nil, err

// if there is a kubeconfig
if kubecfg != "" {
c, err := klient.NewWithKubeConfigFile(kubecfg)
if err != nil {
return nil, err
}
e.client = c
}
e.client = c

return e, nil
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/flags/flags.go
Expand Up @@ -92,7 +92,7 @@ func parseFlags(cmdName string, flags []string) (*Flags, error) {
return nil, err
}

return &Flags{feature: feature, assess: assess, labels: labels}, nil
return &Flags{namespace: namespace, kubeconfig: kubeconfig, feature: feature, assess: assess, labels: labels}, nil
}

type LabelsMap map[string]string
Expand Down

0 comments on commit 074fd98

Please sign in to comment.