Skip to content

Commit

Permalink
Merge pull request #55 from vladimirvivien/table-driven-tests
Browse files Browse the repository at this point in the history
Support for table-driven test representation
  • Loading branch information
vladimirvivien authored Dec 10, 2021
2 parents ea87ca9 + c4a197e commit fee1391
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 7 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
/coverage.html
/coverage.out
.DS_Store
.vscode
2 changes: 2 additions & 0 deletions examples/table/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Table-Driven Tests
This directory contains examples that show how the test framework can be used to define table-driven tests.
95 changes: 95 additions & 0 deletions examples/table/table_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
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 table

import (
"context"
"math/rand"
"os"
"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()
func TestMain(m *testing.M){
// Setup the rand number source and a limit
test.Setup(func(ctx context.Context, config *envconf.Config) (context.Context, error) {
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
return context.WithValue(context.WithValue(ctx, "limit", rand.Int31n(255)), "randsrc", rnd), nil
})

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

func TestTableDriven(t *testing.T) {
// feature 1
table0 := features.Table{
{
Name: "less than equal 64",
Assessment: func(ctx context.Context, t *testing.T, config *envconf.Config) context.Context {
rnd := ctx.Value("randsrc").(*rand.Rand) // in real test, check asserted type
lim := ctx.Value("limit").(int32) // check type assertion
if rnd.Int31n(lim) > 64 {
t.Log("limit should be less than 64")
}
return ctx
},
},
{
Name: "more than than equal 128",
Assessment: func(ctx context.Context, t *testing.T, config *envconf.Config) context.Context {
rnd := ctx.Value("randsrc").(*rand.Rand) // in real test, check asserted type
lim := ctx.Value("limit").(int32) // check type assertion
if rnd.Int31n(lim) > 128 {
t.Log("limit should be less than 128")
}
return ctx
},
},
{
Assessment: func(ctx context.Context, t *testing.T, config *envconf.Config) context.Context {
rnd := ctx.Value("randsrc").(*rand.Rand) // in real test, check asserted type
lim := ctx.Value("limit").(int32) // check type assertion
if rnd.Int31n(lim) > 256 {
t.Log("limit should be less than 256")
}
return ctx
},
},
}.Build("Random numbers").Feature()

// feature 2
table1 := features.Table{
{
Name: "A simple feature",
Assessment: func(ctx context.Context, t *testing.T, config *envconf.Config) context.Context {
rnd := ctx.Value("randsrc").(*rand.Rand)
if rnd.Int() > 100 {
t.Log("this is a great number")
}
return ctx
},
},
}

test.Test(t, table0, table1.Build().Feature() )
}
24 changes: 17 additions & 7 deletions pkg/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ package env
import (
"context"
"fmt"
"math/rand"
"testing"
"time"

log "k8s.io/klog/v2"

Expand All @@ -42,6 +44,7 @@ type testEnv struct {
ctx context.Context
cfg *envconf.Config
actions []action
rnd rand.Source
}

// New creates a test environment with no config attached.
Expand Down Expand Up @@ -89,6 +92,7 @@ func newTestEnv() *testEnv {
return &testEnv{
ctx: context.Background(),
cfg: envconf.New(),
rnd: rand.NewSource(time.Now().UnixNano()),
}
}

Expand Down Expand Up @@ -190,7 +194,7 @@ func (e *testEnv) Test(t *testing.T, testFeatures ...types.Feature) {
// execute each feature
beforeFeatureActions := e.getBeforeFeatureActions()
afterFeatureActions := e.getAfterFeatureActions()
for _, feature := range testFeatures {
for i, feature := range testFeatures {
// execute beforeFeature actions
for _, action := range beforeFeatureActions {
if e.ctx, err = action.runWithFeature(e.ctx, e.cfg, deepCopyFeature(feature)); err != nil {
Expand All @@ -199,7 +203,11 @@ func (e *testEnv) Test(t *testing.T, testFeatures ...types.Feature) {
}

// execute feature test
e.ctx = e.execFeature(e.ctx, t, feature)
featName := feature.Name()
if featName == "" {
featName = fmt.Sprintf("Feature-%d", i+1)
}
e.ctx = e.execFeature(e.ctx, t, featName, feature)

// execute beforeFeature actions
for _, action := range afterFeatureActions {
Expand Down Expand Up @@ -304,9 +312,7 @@ func (e *testEnv) getFinishActions() []action {
return e.getActionsByRole(roleFinish)
}

func (e *testEnv) execFeature(ctx context.Context, t *testing.T, f types.Feature) context.Context {
featName := f.Name()

func (e *testEnv) execFeature(ctx context.Context, t *testing.T, featName string, f types.Feature) context.Context {
// feature-level subtest
t.Run(featName, func(t *testing.T) {
// skip feature which matches with --skip-feature
Expand Down Expand Up @@ -343,8 +349,12 @@ func (e *testEnv) execFeature(ctx context.Context, t *testing.T, f types.Feature
// assessments run as feature/assessment sub level
assessments := features.GetStepsByLevel(f.Steps(), types.LevelAssess)

for _, assess := range assessments {
t.Run(assess.Name(), func(t *testing.T) {
for i, assess := range assessments {
assessName := assess.Name()
if assessName == "" {
assessName = fmt.Sprintf("Assessment-%d", i+1)
}
t.Run(assessName, func(t *testing.T) {
// skip assessments which matches with --skip-assessments
if e.cfg.SkipAssessmentRegex() != nil && e.cfg.SkipAssessmentRegex().MatchString(assess.Name()) {
t.Skipf(`Skipping assessment "%s": name matched`, assess.Name())
Expand Down
49 changes: 49 additions & 0 deletions pkg/features/table.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
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 features

import (
"fmt"
)

// Table provides a structure for table-driven tests.
// Each entry in the table represents an executable assessment.
type Table []struct {
Name string
Assessment Func
}

// Build converts the defined test steps in the table
// into a FeatureBuilder which can be used to add additional attributes
// to the feature before it's exercised. Build takes an optional feature name
// if omitted will be generated.
func (table Table) Build(featureName ...string) *FeatureBuilder {
var name string
if len(featureName) > 0 {
name = featureName[0]
}
f := New(name)
for i, test := range table {
if test.Name == "" {
test.Name = fmt.Sprintf("Assessment-%d", i)
}
if test.Assessment != nil {
f.Assess(test.Name, test.Assessment)
}
}
return f
}

0 comments on commit fee1391

Please sign in to comment.