Skip to content

Commit

Permalink
feat: Remove stretchr/testify dependency (#20)
Browse files Browse the repository at this point in the history
## Motivation

We should strive to make `govy` as pure of a library as possible, with
little to no 3rd party dependencies.
Since Go does not support a concept of developer dependencies, we should
start by removing [testify](https://github.com/stretchr/testify), which
is the heaviest 3rd party dependency for govy.
This way we're not forcing other projects to include the library in
their module's dependency tree.

## Release Notes

Govy no longer relies on `stretchr/testify` library for unit testing,
thus limiting its module's dependency tree size significantly.
  • Loading branch information
nieomylnieja authored Sep 19, 2024
1 parent 73dd5a3 commit c0e1d36
Show file tree
Hide file tree
Showing 28 changed files with 274 additions and 135 deletions.
5 changes: 2 additions & 3 deletions cmd/govy/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ import (
"strings"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/nobl9/govy/internal/assert"

"github.com/nobl9/govy/internal"
)
Expand Down Expand Up @@ -81,7 +80,7 @@ func TestCmd_NameInfer(t *testing.T) {
assert.ElementsMatch(t, expectedCommandOutputLines, strings.Split(strings.TrimSpace(out.String()), "\n"))
generatedFilePath := filepath.Join(tmpDir, fileName)
data, err := os.ReadFile(generatedFilePath)
require.NoError(t, err)
assert.Require(t, assert.NoError(t, err))
assert.Equal(t, fmt.Sprintf(expectedGeneratedFile, tmpDir), string(data))
}

Expand Down
5 changes: 1 addition & 4 deletions docs/validator-comparison/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,9 @@ github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/nobl9/govy v0.1.1 h1:l5UVwLuYhDhDFdmofH+SpE63Ta9IMTusFwY2MeJlCx4=
github.com/nobl9/govy v0.1.1/go.mod h1:MIhQelE3P6Ty7oqXef5ObzsxAo50+RiXmrywNy9ZRRw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI=
Expand Down
4 changes: 0 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,11 @@ module github.com/nobl9/govy
go 1.22

require (
github.com/stretchr/testify v1.9.0
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa
golang.org/x/tools v0.24.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/sync v0.8.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
10 changes: 0 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI=
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
Expand All @@ -12,7 +6,3 @@ golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
12 changes: 2 additions & 10 deletions go.work.sum
Original file line number Diff line number Diff line change
@@ -1,23 +1,15 @@
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457 h1:zf5N6UOrA487eEFacMePxjXAJctxKmyjKUsjA11Uzuk=
golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0=
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
158 changes: 158 additions & 0 deletions internal/assert/assert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package assert

import (
"reflect"
"strings"
"testing"
)

// Require fails the test if the provided boolean is false.
// It should be used in conjunction with assert functions.
// Example:
//
// testutils.Require(t, testutils.AssertError(t, err))
func Require(t *testing.T, isPassing bool) {
t.Helper()
if !isPassing {
t.FailNow()
}
}

// Equal fails the test if the expected and actual values are not equal.
func Equal(t *testing.T, expected, actual interface{}) bool {
t.Helper()
if !areEqual(expected, actual) {
return fail(t, "Expected %v, got %v", expected, actual)
}
return true
}

// True fails the test if the actual value is not true.
func True(t *testing.T, actual bool) bool {
t.Helper()
if !actual {
return fail(t, "Should be true")
}
return true
}

// Len fails the test if the object is not of the expected length.
func Len(t *testing.T, object interface{}, length int) bool {
t.Helper()
if actual := getLen(object); actual != length {
return fail(t, "Expected length %d, got %d", length, actual)
}
return true
}

// IsType fails the test if the object is not of the expected type.
// The expected type is specified using a type parameter.
func IsType[T any](t *testing.T, object interface{}) bool {
t.Helper()
switch object.(type) {
case T:
return true
default:
return fail(t, "Expected type %T, got %T", *new(T), object)
}
}

// Error fails the test if the error is nil.
func Error(t *testing.T, err error) bool {
t.Helper()
if err == nil {
return fail(t, "An error is expected but got nil.")
}
return true
}

// NoError fails the test if the error is not nil.
func NoError(t *testing.T, err error) bool {
t.Helper()
if err != nil {
return fail(t, "Unexpected error:\n%+v", err)
}
return true
}

// EqualError fails the test if the expected error is not equal to the actual error message.
func EqualError(t *testing.T, expected error, actual string) bool {
t.Helper()
if !Error(t, expected) {
return false
}
if expected.Error() != actual {
return fail(t, "Expected error message %q, got %q", expected.Error(), actual)
}
return true
}

// ErrorContains fails the test if the expected error does not contain the provided string.
func ErrorContains(t *testing.T, expected error, contains string) bool {
t.Helper()
if !Error(t, expected) {
return false
}
if !strings.Contains(expected.Error(), contains) {
return fail(t, "Expected error message to contain %q, got %q", contains, expected.Error())
}
return true
}

// ElementsMatch fails the test if the expected and actual slices do not have the same elements.
func ElementsMatch[T comparable](t *testing.T, expected, actual []T) bool {
t.Helper()
if len(expected) != len(actual) {
return fail(t, "Slices are not equal in length, expected: %d, got: %d", len(expected), len(actual))
}

actualVisited := make([]bool, len(actual))
for _, e := range expected {
found := false
for j, a := range actual {
if actualVisited[j] {
continue
}
if areEqual(e, a) {
actualVisited[j] = true
found = true
break
}
}
if !found {
return fail(t, "Expected element %v not found in actual slice", e)
}
}
for i := range actual {
if !actualVisited[i] {
return fail(t, "Unexpected element %v found in actual slice", actual[i])
}
}
return true
}

func areEqual(expected, actual interface{}) bool {
if expected == nil || actual == nil {
return expected == actual
}
if !reflect.DeepEqual(expected, actual) {
return false
}
return true
}

func getLen(v interface{}) int {
rv := reflect.ValueOf(v)
switch rv.Kind() {
case reflect.Slice, reflect.Map, reflect.String:
return rv.Len()
default:
return -1
}
}

func fail(t *testing.T, msg string, a ...interface{}) bool {
t.Helper()
t.Errorf(msg, a...)
return false
}
3 changes: 3 additions & 0 deletions internal/assert/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Package assert provides utility functions for testing
// which help assert certain conditions are met.
package assert
5 changes: 2 additions & 3 deletions pkg/govy/errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import (
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/nobl9/govy/internal/assert"

"github.com/nobl9/govy/internal"
"github.com/nobl9/govy/pkg/govy"
Expand Down Expand Up @@ -344,6 +343,6 @@ func TestHasErrorCode(t *testing.T) {
func expectedErrorOutput(t *testing.T, name string) string {
t.Helper()
data, err := errorsTestData.ReadFile(filepath.Join("test_data", name))
require.NoError(t, err)
assert.Require(t, assert.NoError(t, err))
return string(data)
}
5 changes: 2 additions & 3 deletions pkg/govy/plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import (
"errors"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/nobl9/govy/internal/assert"

"github.com/nobl9/govy/pkg/govy"
"github.com/nobl9/govy/pkg/rules"
Expand Down Expand Up @@ -140,7 +139,7 @@ func TestPlan(t *testing.T) {
enc := json.NewEncoder(&buf)
enc.SetIndent("", " ")
err := enc.Encode(plan)
require.NoError(t, err)
assert.Require(t, assert.NoError(t, err))

assert.Equal(t, expectedPlanJSON, buf.String())
}
15 changes: 7 additions & 8 deletions pkg/govy/rule_set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ package govy_test
import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/nobl9/govy/internal/assert"

"github.com/nobl9/govy/pkg/govy"
"github.com/nobl9/govy/pkg/rules"
Expand All @@ -22,23 +21,23 @@ func TestRuleSet(t *testing.T) {
})
t.Run("first fails", func(t *testing.T) {
err := r.Validate("baz_bar")
require.Error(t, err)
assert.Require(t, assert.Error(t, err))
errs := err.(govy.RuleSetError)
require.Len(t, errs, 1)
assert.Require(t, assert.Len(t, errs, 1))
assert.EqualError(t, errs[0], "string must start with 'foo' prefix")
})
t.Run("second fails", func(t *testing.T) {
err := r.Validate("foo_baz")
require.Error(t, err)
assert.Require(t, assert.Error(t, err))
errs := err.(govy.RuleSetError)
require.Len(t, errs, 1)
assert.Require(t, assert.Len(t, errs, 1))
assert.EqualError(t, errs[0], "string must end with 'bar' suffix")
})
t.Run("both fail", func(t *testing.T) {
err := r.Validate("baz_baz")
require.Error(t, err)
assert.Require(t, assert.Error(t, err))
errs := err.(govy.RuleSetError)
require.Len(t, errs, 2)
assert.Require(t, assert.Len(t, errs, 2))
assert.EqualError(t, errs[0], "string must start with 'foo' prefix")
assert.EqualError(t, errs[1], "string must end with 'bar' suffix")
})
Expand Down
5 changes: 2 additions & 3 deletions pkg/govy/rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import (
"errors"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/nobl9/govy/internal/assert"

"github.com/nobl9/govy/pkg/govy"
)
Expand Down Expand Up @@ -134,7 +133,7 @@ func TestRule_WithDescription(t *testing.T) {
WithDescription("the integer must be positive")

err := r.Validate(-1)
require.Error(t, err)
assert.Require(t, assert.Error(t, err))
assert.Equal(t, &govy.RuleError{
Message: "must be positive",
Code: "test",
Expand Down
Loading

0 comments on commit c0e1d36

Please sign in to comment.