diff --git a/.ci.gofmt.sh b/.ci.gofmt.sh index 3f2e5d329..4c98e8d8e 100755 --- a/.ci.gofmt.sh +++ b/.ci.gofmt.sh @@ -1,8 +1,8 @@ -#!/bin/bash +#!/usr/bin/env bash -if [ -n "$(gofmt -l .)" ]; then +if [ -n "$(go fmt ./...)" ]; then echo "Go code is not formatted:" - gofmt -d . + go fmt ./... exit 1 fi diff --git a/.ci.gogenerate.sh b/.ci.gogenerate.sh index 3fc73fea7..5b5642094 100755 --- a/.ci.gogenerate.sh +++ b/.ci.gogenerate.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # If GOMOD is defined we are running with Go Modules enabled, either # automatically or via the GO111MODULE=on environment variable. Codegen only diff --git a/.ci.govet.sh b/.ci.govet.sh index 77aeb5c47..9bdf4519b 100755 --- a/.ci.govet.sh +++ b/.ci.govet.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e diff --git a/.ci.readme.fmt.sh b/.ci.readme.fmt.sh new file mode 100755 index 000000000..b3d6a1d06 --- /dev/null +++ b/.ci.readme.fmt.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +# Verify that the code snippets in README.md are formatted. +# The tool https://github.com/hougesen/mdsf is used. + +if [ -n "$(mdsf verify --config .mdsf.json --log-level error README.md 2>&1)" ]; then + echo "Go code in the README.md is not formatted." + echo "Did you forget to run 'mdsf format --config .mdsf.json README.md'?" + mdsf format --config .mdsf.json README.md + git diff + exit 1 +fi diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9458c398e..2cd4ad32b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,13 +10,15 @@ jobs: - stable - oldstable steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version: ${{ matrix.go_version }} + - run: npm install -g mdsf-cli - run: ./.ci.gogenerate.sh - run: ./.ci.gofmt.sh + - run: ./.ci.readme.fmt.sh - run: ./.ci.govet.sh - run: go test -v -race ./... test: @@ -29,10 +31,12 @@ jobs: - "1.19" - "1.20" - "1.21" + - "1.22" + - "1.23" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version: ${{ matrix.go_version }} - run: go test -v -race ./... diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 08adf73d1..c094648b0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: contents: write steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Create GitHub release from tag uses: softprops/action-gh-release@v2 diff --git a/.mdsf.json b/.mdsf.json new file mode 100644 index 000000000..884bbbebf --- /dev/null +++ b/.mdsf.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://raw.githubusercontent.com/hougesen/mdsf/main/schemas/v0.8.2/mdsf.schema.json", + "format_finished_document": false, + "languages": { + "go": [ + [ + "gofmt", + "goimports" + ] + ] + } +} diff --git a/MAINTAINERS.md b/MAINTAINERS.md index cb9a39aba..6acd5b1c9 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -6,5 +6,12 @@ pull requests. * @boyan-soubachov * @dolmen * @MovieStoreGuy - * @arjunmahishi * @brackendawson + * @ccoVeille + +## Approvers + +The individuals listed below are active in the project and have the ability to approve pull +requests. + + * @arjunmahishi diff --git a/README.md b/README.md index 0250c3e6d..44d40e6c4 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ Testify - Thou Shalt Write Tests ================================ -ℹ️ We are working on testify v2 and would love to hear what you'd like to see in it, have your say here: https://cutt.ly/testify +> [!NOTE] +> Testify is being maintained at v1, no breaking changes will be accepted in this repo. +> [See discussion about v2](https://github.com/stretchr/testify/discussions/1560). [![Build Status](https://github.com/stretchr/testify/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/stretchr/testify/actions/workflows/main.yml) [![Go Report Card](https://goreportcard.com/badge/github.com/stretchr/testify)](https://goreportcard.com/report/github.com/stretchr/testify) [![PkgGoDev](https://pkg.go.dev/badge/github.com/stretchr/testify)](https://pkg.go.dev/github.com/stretchr/testify) @@ -18,7 +20,7 @@ Get started: * Install testify with [one line of code](#installation), or [update it with another](#staying-up-to-date) * For an introduction to writing test code in Go, see https://go.dev/doc/code#Testing * Check out the API Documentation https://pkg.go.dev/github.com/stretchr/testify - * Use [testifylint](https://github.com/Antonboom/testifylint) (via [golanci-lint](https://golangci-lint.run/)) to avoid common mistakes + * Use [testifylint](https://github.com/Antonboom/testifylint) (via [golangci-lint](https://golangci-lint.run/)) to avoid common mistakes * A little about [Test-Driven Development (TDD)](https://en.wikipedia.org/wiki/Test-driven_development) [`assert`](https://pkg.go.dev/github.com/stretchr/testify/assert "API documentation") package @@ -36,30 +38,27 @@ See it in action: package yours import ( - "testing" - "github.com/stretchr/testify/assert" + "testing" + + "github.com/stretchr/testify/assert" ) func TestSomething(t *testing.T) { + // assert equality + assert.Equal(t, 123, 123, "they should be equal") - // assert equality - assert.Equal(t, 123, 123, "they should be equal") - - // assert inequality - assert.NotEqual(t, 123, 456, "they should not be equal") - - // assert for nil (good for errors) - assert.Nil(t, object) + // assert inequality + assert.NotEqual(t, 123, 456, "they should not be equal") - // assert for not nil (good when you expect something) - if assert.NotNil(t, object) { - - // now we know that object isn't nil, we are safe to make - // further assertions without causing any errors - assert.Equal(t, "Something", object.Value) - - } + // assert for nil (good for errors) + assert.Nil(t, object) + // assert for not nil (good when you expect something) + if assert.NotNil(t, object) { + // now we know that object isn't nil, we are safe to make + // further assertions without causing any errors + assert.Equal(t, "Something", object.Value) + } } ``` @@ -72,29 +71,29 @@ if you assert many times, use the below: package yours import ( - "testing" - "github.com/stretchr/testify/assert" + "testing" + + "github.com/stretchr/testify/assert" ) func TestSomething(t *testing.T) { - assert := assert.New(t) + assert := assert.New(t) - // assert equality - assert.Equal(123, 123, "they should be equal") + // assert equality + assert.Equal(123, 123, "they should be equal") - // assert inequality - assert.NotEqual(123, 456, "they should not be equal") + // assert inequality + assert.NotEqual(123, 456, "they should not be equal") - // assert for nil (good for errors) - assert.Nil(object) + // assert for nil (good for errors) + assert.Nil(object) - // assert for not nil (good when you expect something) - if assert.NotNil(object) { - - // now we know that object isn't nil, we are safe to make - // further assertions without causing any errors - assert.Equal("Something", object.Value) - } + // assert for not nil (good when you expect something) + if assert.NotNil(object) { + // now we know that object isn't nil, we are safe to make + // further assertions without causing any errors + assert.Equal("Something", object.Value) + } } ``` @@ -118,8 +117,9 @@ An example test function that tests a piece of code that relies on an external o package yours import ( - "testing" - "github.com/stretchr/testify/mock" + "testing" + + "github.com/stretchr/testify/mock" ) /* @@ -128,8 +128,8 @@ import ( // MyMockedObject is a mocked object that implements an interface // that describes an object that the code I am testing relies on. -type MyMockedObject struct{ - mock.Mock +type MyMockedObject struct { + mock.Mock } // DoSomething is a method on MyMockedObject that implements some interface @@ -140,10 +140,8 @@ type MyMockedObject struct{ // // NOTE: This method is not being tested here, code that uses this object is. func (m *MyMockedObject) DoSomething(number int) (bool, error) { - - args := m.Called(number) - return args.Bool(0), args.Error(1) - + args := m.Called(number) + return args.Bool(0), args.Error(1) } /* @@ -153,20 +151,17 @@ func (m *MyMockedObject) DoSomething(number int) (bool, error) { // TestSomething is an example of how to use our test object to // make assertions about some target code we are testing. func TestSomething(t *testing.T) { + // create an instance of our test object + testObj := new(MyMockedObject) - // create an instance of our test object - testObj := new(MyMockedObject) - - // set up expectations - testObj.On("DoSomething", 123).Return(true, nil) - - // call the code we are testing - targetFuncThatDoesSomethingWithObj(testObj) - - // assert that the expectations were met - testObj.AssertExpectations(t) + // set up expectations + testObj.On("DoSomething", 123).Return(true, nil) + // call the code we are testing + targetFuncThatDoesSomethingWithObj(testObj) + // assert that the expectations were met + testObj.AssertExpectations(t) } // TestSomethingWithPlaceholder is a second example of how to use our test object to @@ -175,45 +170,42 @@ func TestSomething(t *testing.T) { // data being passed in is normally dynamically generated and cannot be // predicted beforehand (eg. containing hashes that are time sensitive) func TestSomethingWithPlaceholder(t *testing.T) { + // create an instance of our test object + testObj := new(MyMockedObject) - // create an instance of our test object - testObj := new(MyMockedObject) - - // set up expectations with a placeholder in the argument list - testObj.On("DoSomething", mock.Anything).Return(true, nil) + // set up expectations with a placeholder in the argument list + testObj.On("DoSomething", mock.Anything).Return(true, nil) - // call the code we are testing - targetFuncThatDoesSomethingWithObj(testObj) - - // assert that the expectations were met - testObj.AssertExpectations(t) + // call the code we are testing + targetFuncThatDoesSomethingWithObj(testObj) + // assert that the expectations were met + testObj.AssertExpectations(t) } // TestSomethingElse2 is a third example that shows how you can use // the Unset method to cleanup handlers and then add new ones. func TestSomethingElse2(t *testing.T) { + // create an instance of our test object + testObj := new(MyMockedObject) - // create an instance of our test object - testObj := new(MyMockedObject) - - // set up expectations with a placeholder in the argument list - mockCall := testObj.On("DoSomething", mock.Anything).Return(true, nil) + // set up expectations with a placeholder in the argument list + mockCall := testObj.On("DoSomething", mock.Anything).Return(true, nil) - // call the code we are testing - targetFuncThatDoesSomethingWithObj(testObj) + // call the code we are testing + targetFuncThatDoesSomethingWithObj(testObj) - // assert that the expectations were met - testObj.AssertExpectations(t) + // assert that the expectations were met + testObj.AssertExpectations(t) - // remove the handler now so we can add another one that takes precedence - mockCall.Unset() + // remove the handler now so we can add another one that takes precedence + mockCall.Unset() - // return false now instead of true - testObj.On("DoSomething", mock.Anything).Return(false, nil) + // return false now instead of true + testObj.On("DoSomething", mock.Anything).Return(false, nil) - testObj.AssertExpectations(t) + testObj.AssertExpectations(t) } ``` @@ -223,6 +215,8 @@ You can use the [mockery tool](https://vektra.github.io/mockery/latest/) to auto [`suite`](https://pkg.go.dev/github.com/stretchr/testify/suite "API documentation") package ----------------------------------------------------------------------------------------- +> [!WARNING] +> The suite package does not support parallel tests. See [#934](https://github.com/stretchr/testify/issues/934). The `suite` package provides functionality that you might be used to from more common object-oriented languages. With it, you can build a testing suite as a struct, build setup/teardown methods and testing methods on your struct, and run them with 'go test' as per normal. @@ -231,35 +225,36 @@ An example suite is shown below: ```go // Basic imports import ( - "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" ) // Define the suite, and absorb the built-in basic suite // functionality from testify - including a T() method which // returns the current testing context type ExampleTestSuite struct { - suite.Suite - VariableThatShouldStartAtFive int + suite.Suite + VariableThatShouldStartAtFive int } // Make sure that VariableThatShouldStartAtFive is set to five // before each test func (suite *ExampleTestSuite) SetupTest() { - suite.VariableThatShouldStartAtFive = 5 + suite.VariableThatShouldStartAtFive = 5 } // All methods that begin with "Test" are run as tests within a // suite. func (suite *ExampleTestSuite) TestExample() { - assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive) + assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive) } // In order for 'go test' to run this suite, we need to create // a normal test function and pass our suite to suite.Run func TestExampleTestSuite(t *testing.T) { - suite.Run(t, new(ExampleTestSuite)) + suite.Run(t, new(ExampleTestSuite)) } ``` @@ -272,33 +267,34 @@ For more information on writing suites, check out the [API documentation for the ```go // Basic imports import ( - "testing" - "github.com/stretchr/testify/suite" + "testing" + + "github.com/stretchr/testify/suite" ) // Define the suite, and absorb the built-in basic suite // functionality from testify - including assertion methods. type ExampleTestSuite struct { - suite.Suite - VariableThatShouldStartAtFive int + suite.Suite + VariableThatShouldStartAtFive int } // Make sure that VariableThatShouldStartAtFive is set to five // before each test func (suite *ExampleTestSuite) SetupTest() { - suite.VariableThatShouldStartAtFive = 5 + suite.VariableThatShouldStartAtFive = 5 } // All methods that begin with "Test" are run as tests within a // suite. func (suite *ExampleTestSuite) TestExample() { - suite.Equal(suite.VariableThatShouldStartAtFive, 5) + suite.Equal(suite.VariableThatShouldStartAtFive, 5) } // In order for 'go test' to run this suite, we need to create // a normal test function and pass our suite to suite.Run func TestExampleTestSuite(t *testing.T) { - suite.Run(t, new(ExampleTestSuite)) + suite.Run(t, new(ExampleTestSuite)) } ``` @@ -325,14 +321,13 @@ Import the `testify/assert` package into your code using this template: package yours import ( - "testing" - "github.com/stretchr/testify/assert" + "testing" + + "github.com/stretchr/testify/assert" ) func TestSomething(t *testing.T) { - - assert.True(t, true, "True is true!") - + assert.True(t, true, "True is true!") } ``` diff --git a/_codegen/go.mod b/_codegen/go.mod index 31a7e991c..c295a36ba 100644 --- a/_codegen/go.mod +++ b/_codegen/go.mod @@ -1,5 +1,3 @@ module github.com/stretchr/testify/_codegen go 1.11 - -require github.com/ernesto-jimenez/gogen v0.0.0-20180125220232-d7d4131e6607 diff --git a/_codegen/go.sum b/_codegen/go.sum index 1c1b88eb5..e69de29bb 100644 --- a/_codegen/go.sum +++ b/_codegen/go.sum @@ -1,2 +0,0 @@ -github.com/ernesto-jimenez/gogen v0.0.0-20180125220232-d7d4131e6607 h1:cTavhURetDkezJCvxFggiyLeP40Mrk/TtVg2+ycw1Es= -github.com/ernesto-jimenez/gogen v0.0.0-20180125220232-d7d4131e6607/go.mod h1:Cg4fM0vhYWOZdgM7RIOSTRNIc8/VT7CXClC3Ni86lu4= diff --git a/_codegen/internal/imports/imports.go b/_codegen/internal/imports/imports.go new file mode 100644 index 000000000..a0c7e2b6a --- /dev/null +++ b/_codegen/internal/imports/imports.go @@ -0,0 +1,108 @@ +/* +The MIT License (MIT) + +Copyright (c) 2015 Ernesto Jiménez + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package imports + +import ( + "go/types" + "os" + "path/filepath" + "strings" +) + +type Importer interface { + AddImportsFrom(t types.Type) + Imports() map[string]string +} + +// imports contains metadata about all the imports from a given package +type imports struct { + currentpkg string + imp map[string]string +} + +// AddImportsFrom adds imports used in the passed type +func (imp *imports) AddImportsFrom(t types.Type) { + switch el := t.(type) { + case *types.Basic: + case *types.Slice: + imp.AddImportsFrom(el.Elem()) + case *types.Pointer: + imp.AddImportsFrom(el.Elem()) + case *types.Named: + pkg := el.Obj().Pkg() + if pkg == nil { + return + } + if pkg.Name() == imp.currentpkg { + return + } + imp.imp[cleanImportPath(pkg.Path())] = pkg.Name() + case *types.Tuple: + for i := 0; i < el.Len(); i++ { + imp.AddImportsFrom(el.At(i).Type()) + } + default: + } +} + +func cleanImportPath(ipath string) string { + return gopathlessImportPath( + vendorlessImportPath(ipath), + ) +} + +func gopathlessImportPath(ipath string) string { + paths := strings.Split(os.Getenv("GOPATH"), ":") + for _, p := range paths { + ipath = strings.TrimPrefix(ipath, filepath.Join(p, "src")+string(filepath.Separator)) + } + return ipath +} + +// vendorlessImportPath returns the devendorized version of the provided import path. +// e.g. "foo/bar/vendor/a/b" => "a/b" +func vendorlessImportPath(ipath string) string { + // Devendorize for use in import statement. + if i := strings.LastIndex(ipath, "/vendor/"); i >= 0 { + return ipath[i+len("/vendor/"):] + } + if strings.HasPrefix(ipath, "vendor/") { + return ipath[len("vendor/"):] + } + return ipath +} + +// AddImportsFrom adds imports used in the passed type +func (imp *imports) Imports() map[string]string { + return imp.imp +} + +// New initializes a new structure to track packages imported by the currentpkg +func New(currentpkg string) Importer { + return &imports{ + currentpkg: currentpkg, + imp: make(map[string]string), + } +} diff --git a/_codegen/main.go b/_codegen/main.go index 11cdbfbc6..574985181 100644 --- a/_codegen/main.go +++ b/_codegen/main.go @@ -16,7 +16,6 @@ import ( "go/token" "go/types" "io" - "io/ioutil" "log" "os" "path" @@ -24,7 +23,7 @@ import ( "strings" "text/template" - "github.com/ernesto-jimenez/gogen/imports" + "github.com/stretchr/testify/_codegen/internal/imports" ) var ( @@ -101,13 +100,15 @@ func parseTemplates() (*template.Template, *template.Template, error) { return nil, nil, err } if *tmplFile != "" { - f, err := ioutil.ReadFile(*tmplFile) + f, err := os.ReadFile(*tmplFile) if err != nil { return nil, nil, err } funcTemplate = string(f) } - tmpl, err := template.New("function").Parse(funcTemplate) + tmpl, err := template.New("function").Funcs(template.FuncMap{ + "replace": strings.ReplaceAll, + }).Parse(funcTemplate) if err != nil { return nil, nil, err } @@ -181,7 +182,7 @@ func parsePackageSource(pkg string) (*types.Scope, *doc.Package, error) { files := make(map[string]*ast.File) fileList := make([]*ast.File, len(pd.GoFiles)) for i, fname := range pd.GoFiles { - src, err := ioutil.ReadFile(path.Join(pd.Dir, fname)) + src, err := os.ReadFile(path.Join(pd.Dir, fname)) if err != nil { return nil, nil, err } diff --git a/assert/assertion_compare.go b/assert/assertion_compare.go index 7e19eba09..ffb24e8e3 100644 --- a/assert/assertion_compare.go +++ b/assert/assertion_compare.go @@ -390,7 +390,8 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface if h, ok := t.(tHelper); ok { h.Helper() } - return compareTwoValues(t, e1, e2, []compareResult{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) + failMessage := fmt.Sprintf("\"%v\" is not greater than \"%v\"", e1, e2) + return compareTwoValues(t, e1, e2, []compareResult{compareGreater}, failMessage, msgAndArgs...) } // GreaterOrEqual asserts that the first element is greater than or equal to the second @@ -403,7 +404,8 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in if h, ok := t.(tHelper); ok { h.Helper() } - return compareTwoValues(t, e1, e2, []compareResult{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) + failMessage := fmt.Sprintf("\"%v\" is not greater than or equal to \"%v\"", e1, e2) + return compareTwoValues(t, e1, e2, []compareResult{compareGreater, compareEqual}, failMessage, msgAndArgs...) } // Less asserts that the first element is less than the second @@ -415,7 +417,8 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) if h, ok := t.(tHelper); ok { h.Helper() } - return compareTwoValues(t, e1, e2, []compareResult{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) + failMessage := fmt.Sprintf("\"%v\" is not less than \"%v\"", e1, e2) + return compareTwoValues(t, e1, e2, []compareResult{compareLess}, failMessage, msgAndArgs...) } // LessOrEqual asserts that the first element is less than or equal to the second @@ -428,7 +431,8 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter if h, ok := t.(tHelper); ok { h.Helper() } - return compareTwoValues(t, e1, e2, []compareResult{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) + failMessage := fmt.Sprintf("\"%v\" is not less than or equal to \"%v\"", e1, e2) + return compareTwoValues(t, e1, e2, []compareResult{compareLess, compareEqual}, failMessage, msgAndArgs...) } // Positive asserts that the specified element is positive @@ -440,7 +444,8 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { h.Helper() } zero := reflect.Zero(reflect.TypeOf(e)) - return compareTwoValues(t, e, zero.Interface(), []compareResult{compareGreater}, "\"%v\" is not positive", msgAndArgs...) + failMessage := fmt.Sprintf("\"%v\" is not positive", e) + return compareTwoValues(t, e, zero.Interface(), []compareResult{compareGreater}, failMessage, msgAndArgs...) } // Negative asserts that the specified element is negative @@ -452,7 +457,8 @@ func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { h.Helper() } zero := reflect.Zero(reflect.TypeOf(e)) - return compareTwoValues(t, e, zero.Interface(), []compareResult{compareLess}, "\"%v\" is not negative", msgAndArgs...) + failMessage := fmt.Sprintf("\"%v\" is not negative", e) + return compareTwoValues(t, e, zero.Interface(), []compareResult{compareLess}, failMessage, msgAndArgs...) } func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...interface{}) bool { @@ -468,11 +474,11 @@ func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedCompare compareResult, isComparable := compare(e1, e2, e1Kind) if !isComparable { - return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) + return Fail(t, fmt.Sprintf(`Can not compare type "%T"`, e1), msgAndArgs...) } if !containsValue(allowedComparesResults, compareResult) { - return Fail(t, fmt.Sprintf(failMessage, e1, e2), msgAndArgs...) + return Fail(t, failMessage, msgAndArgs...) } return true diff --git a/assert/assertion_compare_test.go b/assert/assertion_compare_test.go index bf6b8160b..d68407a02 100644 --- a/assert/assertion_compare_test.go +++ b/assert/assertion_compare_test.go @@ -10,6 +10,8 @@ import ( ) func TestCompare(t *testing.T) { + t.Parallel() + type customString string type customInt int type customInt8 int8 @@ -127,6 +129,8 @@ func callerName(skip int) string { } func TestGreater(t *testing.T) { + t.Parallel() + mockT := new(testing.T) if !Greater(mockT, 2, 1) { @@ -171,6 +175,8 @@ func TestGreater(t *testing.T) { } func TestGreaterOrEqual(t *testing.T) { + t.Parallel() + mockT := new(testing.T) if !GreaterOrEqual(mockT, 2, 1) { @@ -215,6 +221,8 @@ func TestGreaterOrEqual(t *testing.T) { } func TestLess(t *testing.T) { + t.Parallel() + mockT := new(testing.T) if !Less(mockT, 1, 2) { @@ -259,6 +267,8 @@ func TestLess(t *testing.T) { } func TestLessOrEqual(t *testing.T) { + t.Parallel() + mockT := new(testing.T) if !LessOrEqual(mockT, 1, 2) { @@ -303,6 +313,8 @@ func TestLessOrEqual(t *testing.T) { } func TestPositive(t *testing.T) { + t.Parallel() + mockT := new(testing.T) if !Positive(mockT, 1) { @@ -342,6 +354,8 @@ func TestPositive(t *testing.T) { } func TestNegative(t *testing.T) { + t.Parallel() + mockT := new(testing.T) if !Negative(mockT, -1) { @@ -381,6 +395,8 @@ func TestNegative(t *testing.T) { } func Test_compareTwoValuesDifferentValuesTypes(t *testing.T) { + t.Parallel() + mockT := new(testing.T) for _, currCase := range []struct { @@ -399,6 +415,8 @@ func Test_compareTwoValuesDifferentValuesTypes(t *testing.T) { } func Test_compareTwoValuesNotComparableValues(t *testing.T) { + t.Parallel() + mockT := new(testing.T) type CompareStruct struct { @@ -418,6 +436,8 @@ func Test_compareTwoValuesNotComparableValues(t *testing.T) { } func Test_compareTwoValuesCorrectCompareResult(t *testing.T) { + t.Parallel() + mockT := new(testing.T) for _, currCase := range []struct { @@ -438,6 +458,8 @@ func Test_compareTwoValuesCorrectCompareResult(t *testing.T) { } func Test_containsValue(t *testing.T) { + t.Parallel() + for _, currCase := range []struct { values []compareResult value compareResult diff --git a/assert/assertion_format.go b/assert/assertion_format.go index 546fe1fb2..2d089991a 100644 --- a/assert/assertion_format.go +++ b/assert/assertion_format.go @@ -50,10 +50,19 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string return ElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...) } -// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Emptyf asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // assert.Emptyf(t, obj, "error message %s", "formatted") +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -75,7 +84,7 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar return Equal(t, expected, actual, append([]interface{}{msg}, args...)...) } -// EqualErrorf asserts that a function returned an error (i.e. not `nil`) +// EqualErrorf asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() @@ -115,12 +124,10 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri return EqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) } -// Errorf asserts that a function returned an error (i.e. not `nil`). +// Errorf asserts that a function returned a non-nil error (ie. an error). // -// actualObj, err := SomeFunction() -// if assert.Errorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// assert.Errorf(t, err, "error message %s", "formatted") func Errorf(t TestingT, err error, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -137,8 +144,8 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...) } -// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContainsf asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() // assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") @@ -438,7 +445,19 @@ func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interf return IsNonIncreasing(t, object, append([]interface{}{msg}, args...)...) } +// IsNotTypef asserts that the specified objects are not of the same type. +// +// assert.IsNotTypef(t, &NotMyStruct{}, &MyStruct{}, "error message %s", "formatted") +func IsNotTypef(t TestingT, theType interface{}, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return IsNotType(t, theType, object, append([]interface{}{msg}, args...)...) +} + // IsTypef asserts that the specified objects are of the same type. +// +// assert.IsTypef(t, &MyStruct{}, &MyStruct{}, "error message %s", "formatted") func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -533,7 +552,7 @@ func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) bool return NoDirExists(t, path, append([]interface{}{msg}, args...)...) } -// NoErrorf asserts that a function returned no error (i.e. `nil`). +// NoErrorf asserts that a function returned a nil error (ie. no error). // // actualObj, err := SomeFunction() // if assert.NoErrorf(t, err, "error message %s", "formatted") { @@ -585,8 +604,7 @@ func NotElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg str return NotElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...) } -// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmptyf asserts that the specified object is NOT [Empty]. // // if assert.NotEmptyf(t, obj, "error message %s", "formatted") { // assert.Equal(t, "two", obj[1]) @@ -621,6 +639,15 @@ func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg s return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) } +// NotErrorAsf asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func NotErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotErrorAs(t, err, target, append([]interface{}{msg}, args...)...) +} + // NotErrorIsf asserts that none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { @@ -684,12 +711,15 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, return NotSame(t, expected, actual, append([]interface{}{msg}, args...)...) } -// NotSubsetf asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // assert.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted") // assert.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") +// assert.NotSubsetf(t, [1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted") +// assert.NotSubsetf(t, {"x": 1, "y": 2}, ["z"], "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -773,11 +803,15 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg return Same(t, expected, actual, append([]interface{}{msg}, args...)...) } -// Subsetf asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subsetf asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // assert.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted") // assert.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") +// assert.Subsetf(t, [1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted") +// assert.Subsetf(t, {"x": 1, "y": 2}, ["x"], "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -815,7 +849,19 @@ func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, return WithinRange(t, actual, start, end, append([]interface{}{msg}, args...)...) } -// YAMLEqf asserts that two YAML strings are equivalent. +// YAMLEqf asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// assert.YAMLEqf(t, expected, actual, "error message %s", "formatted") func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/assert/assertion_forward.go b/assert/assertion_forward.go index 8504dca9f..d8300af73 100644 --- a/assert/assertion_forward.go +++ b/assert/assertion_forward.go @@ -92,10 +92,19 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st return ElementsMatchf(a.t, listA, listB, msg, args...) } -// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Empty asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // a.Empty(obj) +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -103,10 +112,19 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { return Empty(a.t, object, msgAndArgs...) } -// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Emptyf asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // a.Emptyf(obj, "error message %s", "formatted") +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -128,7 +146,7 @@ func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs return Equal(a.t, expected, actual, msgAndArgs...) } -// EqualError asserts that a function returned an error (i.e. not `nil`) +// EqualError asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() @@ -140,7 +158,7 @@ func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ... return EqualError(a.t, theError, errString, msgAndArgs...) } -// EqualErrorf asserts that a function returned an error (i.e. not `nil`) +// EqualErrorf asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() @@ -222,12 +240,10 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string return Equalf(a.t, expected, actual, msg, args...) } -// Error asserts that a function returned an error (i.e. not `nil`). +// Error asserts that a function returned a non-nil error (ie. an error). // -// actualObj, err := SomeFunction() -// if a.Error(err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// a.Error(err) func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -253,8 +269,8 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args .. return ErrorAsf(a.t, err, target, msg, args...) } -// ErrorContains asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContains asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() // a.ErrorContains(err, expectedErrorSubString) @@ -265,8 +281,8 @@ func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs . return ErrorContains(a.t, theError, contains, msgAndArgs...) } -// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContainsf asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() // a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") @@ -295,12 +311,10 @@ func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...inter return ErrorIsf(a.t, err, target, msg, args...) } -// Errorf asserts that a function returned an error (i.e. not `nil`). +// Errorf asserts that a function returned a non-nil error (ie. an error). // -// actualObj, err := SomeFunction() -// if a.Errorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// a.Errorf(err, "error message %s", "formatted") func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -868,7 +882,29 @@ func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...in return IsNonIncreasingf(a.t, object, msg, args...) } +// IsNotType asserts that the specified objects are not of the same type. +// +// a.IsNotType(&NotMyStruct{}, &MyStruct{}) +func (a *Assertions) IsNotType(theType interface{}, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsNotType(a.t, theType, object, msgAndArgs...) +} + +// IsNotTypef asserts that the specified objects are not of the same type. +// +// a.IsNotTypef(&NotMyStruct{}, &MyStruct{}, "error message %s", "formatted") +func (a *Assertions) IsNotTypef(theType interface{}, object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsNotTypef(a.t, theType, object, msg, args...) +} + // IsType asserts that the specified objects are of the same type. +// +// a.IsType(&MyStruct{}, &MyStruct{}) func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -877,6 +913,8 @@ func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAnd } // IsTypef asserts that the specified objects are of the same type. +// +// a.IsTypef(&MyStruct{}, &MyStruct{}, "error message %s", "formatted") func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1058,7 +1096,7 @@ func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) return NoDirExistsf(a.t, path, msg, args...) } -// NoError asserts that a function returned no error (i.e. `nil`). +// NoError asserts that a function returned a nil error (ie. no error). // // actualObj, err := SomeFunction() // if a.NoError(err) { @@ -1071,7 +1109,7 @@ func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { return NoError(a.t, err, msgAndArgs...) } -// NoErrorf asserts that a function returned no error (i.e. `nil`). +// NoErrorf asserts that a function returned a nil error (ie. no error). // // actualObj, err := SomeFunction() // if a.NoErrorf(err, "error message %s", "formatted") { @@ -1162,8 +1200,7 @@ func (a *Assertions) NotElementsMatchf(listA interface{}, listB interface{}, msg return NotElementsMatchf(a.t, listA, listB, msg, args...) } -// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmpty asserts that the specified object is NOT [Empty]. // // if a.NotEmpty(obj) { // assert.Equal(t, "two", obj[1]) @@ -1175,8 +1212,7 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) boo return NotEmpty(a.t, object, msgAndArgs...) } -// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmptyf asserts that the specified object is NOT [Empty]. // // if a.NotEmptyf(obj, "error message %s", "formatted") { // assert.Equal(t, "two", obj[1]) @@ -1234,6 +1270,24 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str return NotEqualf(a.t, expected, actual, msg, args...) } +// NotErrorAs asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func (a *Assertions) NotErrorAs(err error, target interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotErrorAs(a.t, err, target, msgAndArgs...) +} + +// NotErrorAsf asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func (a *Assertions) NotErrorAsf(err error, target interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotErrorAsf(a.t, err, target, msg, args...) +} + // NotErrorIs asserts that none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) bool { @@ -1360,12 +1414,15 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri return NotSamef(a.t, expected, actual, msg, args...) } -// NotSubset asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubset asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.NotSubset([1, 3, 4], [1, 2]) // a.NotSubset({"x": 1, "y": 2}, {"z": 3}) +// a.NotSubset([1, 3, 4], {1: "one", 2: "two"}) +// a.NotSubset({"x": 1, "y": 2}, ["z"]) func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1373,12 +1430,15 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs return NotSubset(a.t, list, subset, msgAndArgs...) } -// NotSubsetf asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.NotSubsetf([1, 3, 4], [1, 2], "error message %s", "formatted") // a.NotSubsetf({"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") +// a.NotSubsetf([1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted") +// a.NotSubsetf({"x": 1, "y": 2}, ["z"], "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1538,11 +1598,15 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, return Samef(a.t, expected, actual, msg, args...) } -// Subset asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subset asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.Subset([1, 2, 3], [1, 2]) // a.Subset({"x": 1, "y": 2}, {"x": 1}) +// a.Subset([1, 2, 3], {1: "one", 2: "two"}) +// a.Subset({"x": 1, "y": 2}, ["x"]) func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1550,11 +1614,15 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ... return Subset(a.t, list, subset, msgAndArgs...) } -// Subsetf asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subsetf asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.Subsetf([1, 2, 3], [1, 2], "error message %s", "formatted") // a.Subsetf({"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") +// a.Subsetf([1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted") +// a.Subsetf({"x": 1, "y": 2}, ["x"], "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1622,7 +1690,19 @@ func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Ti return WithinRangef(a.t, actual, start, end, msg, args...) } -// YAMLEq asserts that two YAML strings are equivalent. +// YAMLEq asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// a.YAMLEq(expected, actual) func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1630,7 +1710,19 @@ func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interf return YAMLEq(a.t, expected, actual, msgAndArgs...) } -// YAMLEqf asserts that two YAML strings are equivalent. +// YAMLEqf asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// a.YAMLEqf(expected, actual, "error message %s", "formatted") func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/assert/assertion_order.go b/assert/assertion_order.go index 1d2f71824..a44b40ed3 100644 --- a/assert/assertion_order.go +++ b/assert/assertion_order.go @@ -9,7 +9,7 @@ import ( func isOrdered(t TestingT, object interface{}, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...interface{}) bool { objKind := reflect.TypeOf(object).Kind() if objKind != reflect.Slice && objKind != reflect.Array { - return false + return Fail(t, fmt.Sprintf("object %T is not an ordered collection", object), msgAndArgs...) } objValue := reflect.ValueOf(object) @@ -33,7 +33,7 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []compareR compareResult, isComparable := compare(prevValueInterface, valueInterface, firstValueKind) if !isComparable { - return Fail(t, fmt.Sprintf("Can not compare type \"%s\" and \"%s\"", reflect.TypeOf(value), reflect.TypeOf(prevValue)), msgAndArgs...) + return Fail(t, fmt.Sprintf(`Can not compare type "%T" and "%T"`, value, prevValue), msgAndArgs...) } if !containsValue(allowedComparesResults, compareResult) { @@ -50,6 +50,9 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []compareR // assert.IsIncreasing(t, []float{1, 2}) // assert.IsIncreasing(t, []string{"a", "b"}) func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return isOrdered(t, object, []compareResult{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) } @@ -59,6 +62,9 @@ func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo // assert.IsNonIncreasing(t, []float{2, 1}) // assert.IsNonIncreasing(t, []string{"b", "a"}) func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return isOrdered(t, object, []compareResult{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) } @@ -68,6 +74,9 @@ func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) // assert.IsDecreasing(t, []float{2, 1}) // assert.IsDecreasing(t, []string{"b", "a"}) func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return isOrdered(t, object, []compareResult{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) } @@ -77,5 +86,8 @@ func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo // assert.IsNonDecreasing(t, []float{1, 2}) // assert.IsNonDecreasing(t, []string{"a", "b"}) func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return isOrdered(t, object, []compareResult{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) } diff --git a/assert/assertion_order_test.go b/assert/assertion_order_test.go index eefe06127..f6a4763fa 100644 --- a/assert/assertion_order_test.go +++ b/assert/assertion_order_test.go @@ -2,10 +2,13 @@ package assert import ( "bytes" + "fmt" "testing" ) func TestIsIncreasing(t *testing.T) { + t.Parallel() + mockT := new(testing.T) if !IsIncreasing(mockT, []int{1, 2}) { @@ -43,14 +46,19 @@ func TestIsIncreasing(t *testing.T) { {collection: []uint64{2, 1}, msg: `"2" is not less than "1"`}, {collection: []float32{2.34, 1.23}, msg: `"2.34" is not less than "1.23"`}, {collection: []float64{2.34, 1.23}, msg: `"2.34" is not less than "1.23"`}, + {collection: struct{}{}, msg: `object struct {} is not an ordered collection`}, } { - out := &outputT{buf: bytes.NewBuffer(nil)} - False(t, IsIncreasing(out, currCase.collection)) - Contains(t, out.buf.String(), currCase.msg) + t.Run(fmt.Sprintf("%#v", currCase.collection), func(t *testing.T) { + out := &outputT{buf: bytes.NewBuffer(nil)} + False(t, IsIncreasing(out, currCase.collection)) + Contains(t, out.buf.String(), currCase.msg) + }) } } func TestIsNonIncreasing(t *testing.T) { + t.Parallel() + mockT := new(testing.T) if !IsNonIncreasing(mockT, []int{2, 1}) { @@ -88,14 +96,19 @@ func TestIsNonIncreasing(t *testing.T) { {collection: []uint64{1, 2}, msg: `"1" is not greater than or equal to "2"`}, {collection: []float32{1.23, 2.34}, msg: `"1.23" is not greater than or equal to "2.34"`}, {collection: []float64{1.23, 2.34}, msg: `"1.23" is not greater than or equal to "2.34"`}, + {collection: struct{}{}, msg: `object struct {} is not an ordered collection`}, } { - out := &outputT{buf: bytes.NewBuffer(nil)} - False(t, IsNonIncreasing(out, currCase.collection)) - Contains(t, out.buf.String(), currCase.msg) + t.Run(fmt.Sprintf("%#v", currCase.collection), func(t *testing.T) { + out := &outputT{buf: bytes.NewBuffer(nil)} + False(t, IsNonIncreasing(out, currCase.collection)) + Contains(t, out.buf.String(), currCase.msg) + }) } } func TestIsDecreasing(t *testing.T) { + t.Parallel() + mockT := new(testing.T) if !IsDecreasing(mockT, []int{2, 1}) { @@ -133,14 +146,19 @@ func TestIsDecreasing(t *testing.T) { {collection: []uint64{1, 2}, msg: `"1" is not greater than "2"`}, {collection: []float32{1.23, 2.34}, msg: `"1.23" is not greater than "2.34"`}, {collection: []float64{1.23, 2.34}, msg: `"1.23" is not greater than "2.34"`}, + {collection: struct{}{}, msg: `object struct {} is not an ordered collection`}, } { - out := &outputT{buf: bytes.NewBuffer(nil)} - False(t, IsDecreasing(out, currCase.collection)) - Contains(t, out.buf.String(), currCase.msg) + t.Run(fmt.Sprintf("%#v", currCase.collection), func(t *testing.T) { + out := &outputT{buf: bytes.NewBuffer(nil)} + False(t, IsDecreasing(out, currCase.collection)) + Contains(t, out.buf.String(), currCase.msg) + }) } } func TestIsNonDecreasing(t *testing.T) { + t.Parallel() + mockT := new(testing.T) if !IsNonDecreasing(mockT, []int{1, 2}) { @@ -178,14 +196,19 @@ func TestIsNonDecreasing(t *testing.T) { {collection: []uint64{2, 1}, msg: `"2" is not less than or equal to "1"`}, {collection: []float32{2.34, 1.23}, msg: `"2.34" is not less than or equal to "1.23"`}, {collection: []float64{2.34, 1.23}, msg: `"2.34" is not less than or equal to "1.23"`}, + {collection: struct{}{}, msg: `object struct {} is not an ordered collection`}, } { - out := &outputT{buf: bytes.NewBuffer(nil)} - False(t, IsNonDecreasing(out, currCase.collection)) - Contains(t, out.buf.String(), currCase.msg) + t.Run(fmt.Sprintf("%#v", currCase.collection), func(t *testing.T) { + out := &outputT{buf: bytes.NewBuffer(nil)} + False(t, IsNonDecreasing(out, currCase.collection)) + Contains(t, out.buf.String(), currCase.msg) + }) } } func TestOrderingMsgAndArgsForwarding(t *testing.T) { + t.Parallel() + msgAndArgs := []interface{}{"format %s %x", "this", 0xc001} expectedOutput := "format this c001\n" collection := []int{1, 2, 1} diff --git a/assert/assertions.go b/assert/assertions.go index 104a0c936..a27e70546 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -18,7 +18,7 @@ import ( "unicode/utf8" "github.com/davecgh/go-spew/spew" - "github.com/pmezard/go-difflib/difflib" + "github.com/stretchr/testify/internal/difflib" // Wrapper around gopkg.in/yaml.v3 "github.com/stretchr/testify/assert/yaml" @@ -210,59 +210,77 @@ the problem actually occurred in calling code.*/ // of each stack frame leading from the current test to the assert call that // failed. func CallerInfo() []string { - var pc uintptr - var ok bool var file string var line int var name string + const stackFrameBufferSize = 10 + pcs := make([]uintptr, stackFrameBufferSize) + callers := []string{} - for i := 0; ; i++ { - pc, file, line, ok = runtime.Caller(i) - if !ok { - // The breaks below failed to terminate the loop, and we ran off the - // end of the call stack. - break - } + offset := 1 - // This is a huge edge case, but it will panic if this is the case, see #180 - if file == "" { - break - } + for { + n := runtime.Callers(offset, pcs) - f := runtime.FuncForPC(pc) - if f == nil { - break - } - name = f.Name() - - // testing.tRunner is the standard library function that calls - // tests. Subtests are called directly by tRunner, without going through - // the Test/Benchmark/Example function that contains the t.Run calls, so - // with subtests we should break when we hit tRunner, without adding it - // to the list of callers. - if name == "testing.tRunner" { + if n == 0 { break } - parts := strings.Split(file, "/") - if len(parts) > 1 { - filename := parts[len(parts)-1] - dir := parts[len(parts)-2] - if (dir != "assert" && dir != "mock" && dir != "require") || filename == "mock_test.go" { - callers = append(callers, fmt.Sprintf("%s:%d", file, line)) + frames := runtime.CallersFrames(pcs[:n]) + + for { + frame, more := frames.Next() + pc = frame.PC + file = frame.File + line = frame.Line + + // This is a huge edge case, but it will panic if this is the case, see #180 + if file == "" { + break } - } - // Drop the package - segments := strings.Split(name, ".") - name = segments[len(segments)-1] - if isTest(name, "Test") || - isTest(name, "Benchmark") || - isTest(name, "Example") { - break + f := runtime.FuncForPC(pc) + if f == nil { + break + } + name = f.Name() + + // testing.tRunner is the standard library function that calls + // tests. Subtests are called directly by tRunner, without going through + // the Test/Benchmark/Example function that contains the t.Run calls, so + // with subtests we should break when we hit tRunner, without adding it + // to the list of callers. + if name == "testing.tRunner" { + break + } + + parts := strings.Split(file, "/") + if len(parts) > 1 { + filename := parts[len(parts)-1] + dir := parts[len(parts)-2] + if (dir != "assert" && dir != "mock" && dir != "require") || filename == "mock_test.go" { + callers = append(callers, fmt.Sprintf("%s:%d", file, line)) + } + } + + // Drop the package + dotPos := strings.LastIndexByte(name, '.') + name = name[dotPos+1:] + if isTest(name, "Test") || + isTest(name, "Benchmark") || + isTest(name, "Example") { + break + } + + if !more { + break + } } + + // Next batch + offset += cap(pcs) } return callers @@ -307,13 +325,15 @@ func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { func indentMessageLines(message string, longestLabelLen int) string { outBuf := new(bytes.Buffer) - for i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ { - // no need to align first line because it starts at the correct location (after the label) - if i != 0 { - // append alignLen+1 spaces to align with "{{longestLabel}}:" before adding tab - outBuf.WriteString("\n\t" + strings.Repeat(" ", longestLabelLen+1) + "\t") + scanner := bufio.NewScanner(strings.NewReader(message)) + for firstLine := true; scanner.Scan(); firstLine = false { + if !firstLine { + fmt.Fprint(outBuf, "\n\t"+strings.Repeat(" ", longestLabelLen+1)+"\t") } - outBuf.WriteString(scanner.Text()) + fmt.Fprint(outBuf, scanner.Text()) + } + if err := scanner.Err(); err != nil { + return fmt.Sprintf("cannot display message: %s", err) } return outBuf.String() @@ -437,17 +457,34 @@ func NotImplements(t TestingT, interfaceObject interface{}, object interface{}, return true } +func isType(expectedType, object interface{}) bool { + return ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) +} + // IsType asserts that the specified objects are of the same type. -func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { +// +// assert.IsType(t, &MyStruct{}, &MyStruct{}) +func IsType(t TestingT, expectedType, object interface{}, msgAndArgs ...interface{}) bool { + if isType(expectedType, object) { + return true + } if h, ok := t.(tHelper); ok { h.Helper() } + return Fail(t, fmt.Sprintf("Object expected to be of type %T, but was %T", expectedType, object), msgAndArgs...) +} - if !ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) { - return Fail(t, fmt.Sprintf("Object expected to be of type %v, but was %v", reflect.TypeOf(expectedType), reflect.TypeOf(object)), msgAndArgs...) +// IsNotType asserts that the specified objects are not of the same type. +// +// assert.IsNotType(t, &NotMyStruct{}, &MyStruct{}) +func IsNotType(t TestingT, theType, object interface{}, msgAndArgs ...interface{}) bool { + if !isType(theType, object) { + return true } - - return true + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Fail(t, fmt.Sprintf("Object type expected to be different than %T", theType), msgAndArgs...) } // Equal asserts that two objects are equal. @@ -475,7 +512,6 @@ func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) } return true - } // validateEqualArgs checks whether provided arguments can be safely used in the @@ -502,10 +538,16 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b h.Helper() } - if !samePointers(expected, actual) { + same, ok := samePointers(expected, actual) + if !ok { + return Fail(t, "Both arguments must be pointers", msgAndArgs...) + } + + if !same { + // both are pointers but not the same type & pointing to the same address return Fail(t, fmt.Sprintf("Not same: \n"+ - "expected: %p %#v\n"+ - "actual : %p %#v", expected, expected, actual, actual), msgAndArgs...) + "expected: %[2]s (%[1]T)(%[1]p)\n"+ + "actual : %[4]s (%[3]T)(%[3]p)", expected, truncatingFormat("%#v", expected), actual, truncatingFormat("%#v", actual)), msgAndArgs...) } return true @@ -522,29 +564,37 @@ func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} h.Helper() } - if samePointers(expected, actual) { + same, ok := samePointers(expected, actual) + if !ok { + // fails when the arguments are not pointers + return !(Fail(t, "Both arguments must be pointers", msgAndArgs...)) + } + + if same { return Fail(t, fmt.Sprintf( - "Expected and actual point to the same object: %p %#v", - expected, expected), msgAndArgs...) + "Expected and actual point to the same object: %p %s", + expected, truncatingFormat("%#v", expected)), msgAndArgs...) } return true } -// samePointers compares two generic interface objects and returns whether -// they point to the same object -func samePointers(first, second interface{}) bool { +// samePointers checks if two generic interface objects are pointers of the same +// type pointing to the same object. It returns two values: same indicating if +// they are the same type and point to the same object, and ok indicating that +// both inputs are pointers. +func samePointers(first, second interface{}) (same bool, ok bool) { firstPtr, secondPtr := reflect.ValueOf(first), reflect.ValueOf(second) if firstPtr.Kind() != reflect.Ptr || secondPtr.Kind() != reflect.Ptr { - return false + return false, false // not both are pointers } firstType, secondType := reflect.TypeOf(first), reflect.TypeOf(second) if firstType != secondType { - return false + return false, true // both are pointers, but of different types } // compare pointer addresses - return first == second + return first == second, true } // formatUnequalValues takes two values of arbitrary types and returns string @@ -555,25 +605,26 @@ func samePointers(first, second interface{}) bool { // to a type conversion in the Go grammar. func formatUnequalValues(expected, actual interface{}) (e string, a string) { if reflect.TypeOf(expected) != reflect.TypeOf(actual) { - return fmt.Sprintf("%T(%s)", expected, truncatingFormat(expected)), - fmt.Sprintf("%T(%s)", actual, truncatingFormat(actual)) + return fmt.Sprintf("%T(%s)", expected, truncatingFormat("%#v", expected)), + fmt.Sprintf("%T(%s)", actual, truncatingFormat("%#v", actual)) } switch expected.(type) { case time.Duration: return fmt.Sprintf("%v", expected), fmt.Sprintf("%v", actual) } - return truncatingFormat(expected), truncatingFormat(actual) + return truncatingFormat("%#v", expected), truncatingFormat("%#v", actual) } // truncatingFormat formats the data and truncates it if it's too long. // // This helps keep formatted error messages lines from exceeding the // bufio.MaxScanTokenSize max line length that the go testing framework imposes. -func truncatingFormat(data interface{}) string { - value := fmt.Sprintf("%#v", data) - max := bufio.MaxScanTokenSize - 100 // Give us some space the type info too if needed. - if len(value) > max { - value = value[0:max] + "<... truncated>" +func truncatingFormat(format string, data interface{}) string { + value := fmt.Sprintf(format, data) + // Give us space for two truncated objects and the surrounding sentence. + maxMessageSize := bufio.MaxScanTokenSize/2 - 100 + if len(value) > maxMessageSize { + value = value[0:maxMessageSize] + "<... truncated>" } return value } @@ -596,7 +647,6 @@ func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interfa } return true - } // EqualExportedValues asserts that the types of two objects are equal and their public @@ -621,21 +671,6 @@ func EqualExportedValues(t TestingT, expected, actual interface{}, msgAndArgs .. return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...) } - if aType.Kind() == reflect.Ptr { - aType = aType.Elem() - } - if bType.Kind() == reflect.Ptr { - bType = bType.Elem() - } - - if aType.Kind() != reflect.Struct { - return Fail(t, fmt.Sprintf("Types expected to both be struct or pointer to struct \n\t%v != %v", aType.Kind(), reflect.Struct), msgAndArgs...) - } - - if bType.Kind() != reflect.Struct { - return Fail(t, fmt.Sprintf("Types expected to both be struct or pointer to struct \n\t%v != %v", bType.Kind(), reflect.Struct), msgAndArgs...) - } - expected = copyExportedFields(expected) actual = copyExportedFields(actual) @@ -666,7 +701,6 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} } return Equal(t, expected, actual, msgAndArgs...) - } // NotNil asserts that the specified object is not nil. @@ -711,57 +745,63 @@ func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } - return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...) + return Fail(t, fmt.Sprintf("Expected nil, but got: %s", truncatingFormat("%#v", object)), msgAndArgs...) } // isEmpty gets whether the specified object is considered empty or not. func isEmpty(object interface{}) bool { - // get nil case out of the way if object == nil { return true } - objValue := reflect.ValueOf(object) + return isEmptyValue(reflect.ValueOf(object)) +} +// isEmptyValue gets whether the specified reflect.Value is considered empty or not. +func isEmptyValue(objValue reflect.Value) bool { + if objValue.IsZero() { + return true + } + // Special cases of non-zero values that we consider empty switch objValue.Kind() { // collection types are empty when they have no element + // Note: array types are empty when they match their zero-initialized state. case reflect.Chan, reflect.Map, reflect.Slice: return objValue.Len() == 0 - // pointers are empty if nil or if the value they point to is empty + // non-nil pointers are empty if the value they point to is empty case reflect.Ptr: - if objValue.IsNil() { - return true - } - deref := objValue.Elem().Interface() - return isEmpty(deref) - // for all other types, compare against the zero value - // array types are empty when they match their zero-initialized state - default: - zero := reflect.Zero(objValue.Type()) - return reflect.DeepEqual(object, zero.Interface()) + return isEmptyValue(objValue.Elem()) } + return false } -// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Empty asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // assert.Empty(t, obj) +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { pass := isEmpty(object) if !pass { if h, ok := t.(tHelper); ok { h.Helper() } - Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...) + Fail(t, fmt.Sprintf("Should be empty, but was %s", truncatingFormat("%v", object)), msgAndArgs...) } return pass - } -// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmpty asserts that the specified object is NOT [Empty]. // // if assert.NotEmpty(t, obj) { // assert.Equal(t, "two", obj[1]) @@ -776,7 +816,6 @@ func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { } return pass - } // getLen tries to get the length of an object. @@ -799,11 +838,11 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) } l, ok := getLen(object) if !ok { - return Fail(t, fmt.Sprintf("\"%v\" could not be applied builtin len()", object), msgAndArgs...) + return Fail(t, fmt.Sprintf("%q could not be applied builtin len()", truncatingFormat("%v", object)), msgAndArgs...) } if l != length { - return Fail(t, fmt.Sprintf("\"%v\" should have %d item(s), but has %d", object, length, l), msgAndArgs...) + return Fail(t, fmt.Sprintf("%q should have %d item(s), but has %d", truncatingFormat("%v", object), length, l), msgAndArgs...) } return true } @@ -820,7 +859,6 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { } return true - } // False asserts that the specified value is false. @@ -835,7 +873,6 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { } return true - } // NotEqual asserts that the specified values are NOT equal. @@ -854,11 +891,10 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{ } if ObjectsAreEqual(expected, actual) { - return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...) + return Fail(t, fmt.Sprintf("Should not be: %s\n", truncatingFormat("%#v", actual)), msgAndArgs...) } return true - } // NotEqualValues asserts that two objects are not equal even when converted to the same type @@ -870,7 +906,7 @@ func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...inte } if ObjectsAreEqualValues(expected, actual) { - return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...) + return Fail(t, fmt.Sprintf("Should not be: %s\n", truncatingFormat("%#v", actual)), msgAndArgs...) } return true @@ -881,7 +917,6 @@ func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...inte // return (true, false) if element was not found. // return (true, true) if element was found. func containsElement(list interface{}, element interface{}) (ok, found bool) { - listValue := reflect.ValueOf(list) listType := reflect.TypeOf(list) if listType == nil { @@ -916,7 +951,6 @@ func containsElement(list interface{}, element interface{}) (ok, found bool) { } } return true, false - } // Contains asserts that the specified string, list(array, slice...) or map contains the @@ -932,14 +966,13 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo ok, found := containsElement(s, contains) if !ok { - return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s could not be applied builtin len()", truncatingFormat("%#v", s)), msgAndArgs...) } if !found { - return Fail(t, fmt.Sprintf("%#v does not contain %#v", s, contains), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", s), contains), msgAndArgs...) } return true - } // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the @@ -955,21 +988,24 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) ok, found := containsElement(s, contains) if !ok { - return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s could not be applied builtin len()", truncatingFormat("%#v", s)), msgAndArgs...) } if found { - return Fail(t, fmt.Sprintf("%#v should not contain %#v", s, contains), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s should not contain %#v", truncatingFormat("%#v", s), contains), msgAndArgs...) } return true - } -// Subset asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subset asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // assert.Subset(t, [1, 2, 3], [1, 2]) // assert.Subset(t, {"x": 1, "y": 2}, {"x": 1}) +// assert.Subset(t, [1, 2, 3], {1: "one", 2: "two"}) +// assert.Subset(t, {"x": 1, "y": 2}, ["x"]) func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() @@ -984,7 +1020,7 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok } subsetKind := reflect.TypeOf(subset).Kind() - if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { + if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } @@ -997,10 +1033,10 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok av := actualMap.MapIndex(k) if !av.IsValid() { - return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s does not contain %s", truncatingFormat("%#v", list), truncatingFormat("%#v", subset)), msgAndArgs...) } if !ObjectsAreEqual(ev.Interface(), av.Interface()) { - return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s does not contain %s", truncatingFormat("%#v", list), truncatingFormat("%#v", subset)), msgAndArgs...) } } @@ -1008,6 +1044,13 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok } subsetList := reflect.ValueOf(subset) + if subsetKind == reflect.Map { + keys := make([]interface{}, subsetList.Len()) + for idx, key := range subsetList.MapKeys() { + keys[idx] = key.Interface() + } + subsetList = reflect.ValueOf(keys) + } for i := 0; i < subsetList.Len(); i++ { element := subsetList.Index(i).Interface() ok, found := containsElement(list, element) @@ -1015,19 +1058,22 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", list), msgAndArgs...) } if !found { - return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, element), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", list), element), msgAndArgs...) } } return true } -// NotSubset asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubset asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // assert.NotSubset(t, [1, 3, 4], [1, 2]) // assert.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) +// assert.NotSubset(t, [1, 3, 4], {1: "one", 2: "two"}) +// assert.NotSubset(t, {"x": 1, "y": 2}, ["z"]) func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1042,7 +1088,7 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) } subsetKind := reflect.TypeOf(subset).Kind() - if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { + if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } @@ -1062,22 +1108,29 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) } } - return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s is a subset of %s", truncatingFormat("%q", subset), truncatingFormat("%q", list)), msgAndArgs...) } subsetList := reflect.ValueOf(subset) + if subsetKind == reflect.Map { + keys := make([]interface{}, subsetList.Len()) + for idx, key := range subsetList.MapKeys() { + keys[idx] = key.Interface() + } + subsetList = reflect.ValueOf(keys) + } for i := 0; i < subsetList.Len(); i++ { element := subsetList.Index(i).Interface() ok, found := containsElement(list, element) if !ok { - return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...) + return Fail(t, fmt.Sprintf("%q could not be applied builtin len()", list), msgAndArgs...) } if !found { return true } } - return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s is a subset of %s", truncatingFormat("%q", subset), truncatingFormat("%q", list)), msgAndArgs...) } // ElementsMatch asserts that the specified listA(array, slice...) is equal to specified @@ -1292,9 +1345,15 @@ func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs . if !funcDidPanic { return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) } - panicErr, ok := panicValue.(error) - if !ok || panicErr.Error() != errString { - return Fail(t, fmt.Sprintf("func %#v should panic with error message:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", f, errString, panicValue, panickedStack), msgAndArgs...) + panicErr, isError := panicValue.(error) + if !isError || panicErr.Error() != errString { + msg := fmt.Sprintf("func %#v should panic with error message:\t%#v\n", f, errString) + if isError { + msg += fmt.Sprintf("\tError message:\t%#v\n", panicErr.Error()) + } + msg += fmt.Sprintf("\tPanic value:\t%#v\n", panicValue) + msg += fmt.Sprintf("\tPanic stack:\t%s\n", panickedStack) + return Fail(t, msg, msgAndArgs...) } return true @@ -1527,6 +1586,9 @@ func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAnd if err != nil { return Fail(t, err.Error(), msgAndArgs...) } + if math.IsNaN(actualEpsilon) { + return Fail(t, "relative error is NaN", msgAndArgs...) + } if actualEpsilon > epsilon { return Fail(t, fmt.Sprintf("Relative error is too high: %#v (expected)\n"+ " < %#v (actual)", epsilon, actualEpsilon), msgAndArgs...) @@ -1570,7 +1632,7 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m Errors */ -// NoError asserts that a function returned no error (i.e. `nil`). +// NoError asserts that a function returned a nil error (ie. no error). // // actualObj, err := SomeFunction() // if assert.NoError(t, err) { @@ -1581,18 +1643,16 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } - return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...) + return Fail(t, fmt.Sprintf("Received unexpected error:\n%s", truncatingFormat("%+v", err)), msgAndArgs...) } return true } -// Error asserts that a function returned an error (i.e. not `nil`). +// Error asserts that a function returned a non-nil error (ie. an error). // -// actualObj, err := SomeFunction() -// if assert.Error(t, err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// assert.Error(t, err) func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { if err == nil { if h, ok := t.(tHelper); ok { @@ -1604,7 +1664,7 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { return true } -// EqualError asserts that a function returned an error (i.e. not `nil`) +// EqualError asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() @@ -1622,13 +1682,13 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte if expected != actual { return Fail(t, fmt.Sprintf("Error message not equal:\n"+ "expected: %q\n"+ - "actual : %q", expected, actual), msgAndArgs...) + "actual : %s", expected, truncatingFormat("%q", actual)), msgAndArgs...) } return true } -// ErrorContains asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContains asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() // assert.ErrorContains(t, err, expectedErrorSubString) @@ -1642,7 +1702,7 @@ func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...in actual := theError.Error() if !strings.Contains(actual, contains) { - return Fail(t, fmt.Sprintf("Error %#v does not contain %#v", actual, contains), msgAndArgs...) + return Fail(t, fmt.Sprintf("Error %s does not contain %#v", truncatingFormat("%#v", actual), contains), msgAndArgs...) } return true @@ -1665,7 +1725,6 @@ func matchRegexp(rx interface{}, str interface{}) bool { default: return r.MatchString(fmt.Sprint(v)) } - } // Regexp asserts that a specified regexp matches a string. @@ -1701,7 +1760,6 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf } return !match - } // Zero asserts that i is the zero value for its type. @@ -1710,7 +1768,7 @@ func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { h.Helper() } if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { - return Fail(t, fmt.Sprintf("Should be zero, but was %v", i), msgAndArgs...) + return Fail(t, fmt.Sprintf("Should be zero, but was %s", truncatingFormat("%v", i)), msgAndArgs...) } return true } @@ -1812,6 +1870,11 @@ func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{ return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid json.\nJSON parsing error: '%s'", expected, err.Error()), msgAndArgs...) } + // Shortcut if same bytes + if actual == expected { + return true + } + if err := json.Unmarshal([]byte(actual), &actualJSONAsInterface); err != nil { return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid json.\nJSON parsing error: '%s'", actual, err.Error()), msgAndArgs...) } @@ -1819,7 +1882,19 @@ func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{ return Equal(t, expectedJSONAsInterface, actualJSONAsInterface, msgAndArgs...) } -// YAMLEq asserts that two YAML strings are equivalent. +// YAMLEq asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// assert.YAMLEq(t, expected, actual) func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1830,6 +1905,11 @@ func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{ return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid yaml.\nYAML parsing error: '%s'", expected, err.Error()), msgAndArgs...) } + // Shortcut if same bytes + if actual == expected { + return true + } + if err := yaml.Unmarshal([]byte(actual), &actualYAMLAsInterface); err != nil { return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid yaml.\nYAML error: '%s'", actual, err.Error()), msgAndArgs...) } @@ -1931,6 +2011,7 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t } ch := make(chan bool, 1) + checkCond := func() { ch <- condition() } timer := time.NewTimer(waitFor) defer timer.Stop() @@ -1938,18 +2019,23 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t ticker := time.NewTicker(tick) defer ticker.Stop() - for tick := ticker.C; ; { + var tickC <-chan time.Time + + // Check the condition once first on the initial call. + go checkCond() + + for { select { case <-timer.C: return Fail(t, "Condition never satisfied", msgAndArgs...) - case <-tick: - tick = nil - go func() { ch <- condition() }() + case <-tickC: + tickC = nil + go checkCond() case v := <-ch: if v { return true } - tick = ticker.C + tickC = ticker.C } } } @@ -1962,6 +2048,9 @@ type CollectT struct { errors []error } +// Helper is like [testing.T.Helper] but does nothing. +func (CollectT) Helper() {} + // Errorf collects the error. func (c *CollectT) Errorf(format string, args ...interface{}) { c.errors = append(c.errors, fmt.Errorf(format, args...)) @@ -2019,35 +2108,42 @@ func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time var lastFinishedTickErrs []error ch := make(chan *CollectT, 1) + checkCond := func() { + collect := new(CollectT) + defer func() { + ch <- collect + }() + condition(collect) + } + timer := time.NewTimer(waitFor) defer timer.Stop() ticker := time.NewTicker(tick) defer ticker.Stop() - for tick := ticker.C; ; { + var tickC <-chan time.Time + + // Check the condition once first on the initial call. + go checkCond() + + for { select { case <-timer.C: for _, err := range lastFinishedTickErrs { t.Errorf("%v", err) } return Fail(t, "Condition never satisfied", msgAndArgs...) - case <-tick: - tick = nil - go func() { - collect := new(CollectT) - defer func() { - ch <- collect - }() - condition(collect) - }() + case <-tickC: + tickC = nil + go checkCond() case collect := <-ch: if !collect.failed() { return true } // Keep the errors from the last ended condition, so that they can be copied to t if timeout is reached. lastFinishedTickErrs = collect.errors - tick = ticker.C + tickC = ticker.C } } } @@ -2062,6 +2158,7 @@ func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.D } ch := make(chan bool, 1) + checkCond := func() { ch <- condition() } timer := time.NewTimer(waitFor) defer timer.Stop() @@ -2069,18 +2166,23 @@ func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.D ticker := time.NewTicker(tick) defer ticker.Stop() - for tick := ticker.C; ; { + var tickC <-chan time.Time + + // Check the condition once first on the initial call. + go checkCond() + + for { select { case <-timer.C: return true - case <-tick: - tick = nil - go func() { ch <- condition() }() + case <-tickC: + tickC = nil + go checkCond() case v := <-ch: if v { return Fail(t, "Condition satisfied", msgAndArgs...) } - tick = ticker.C + tickC = ticker.C } } } @@ -2098,13 +2200,16 @@ func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { var expectedText string if target != nil { expectedText = target.Error() + if err == nil { + return Fail(t, fmt.Sprintf("Expected error with %q in chain but got nil.", expectedText), msgAndArgs...) + } } - chain := buildErrorChainString(err) + chain := buildErrorChainString(err, false) return Fail(t, fmt.Sprintf("Target error should be in err chain:\n"+ - "expected: %q\n"+ - "in chain: %s", expectedText, chain, + "expected: %s\n"+ + "in chain: %s", truncatingFormat("%q", expectedText), truncatingFormat("%s", chain), ), msgAndArgs...) } @@ -2123,11 +2228,11 @@ func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { expectedText = target.Error() } - chain := buildErrorChainString(err) + chain := buildErrorChainString(err, false) return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ - "found: %q\n"+ - "in chain: %s", expectedText, chain, + "found: %s\n"+ + "in chain: %s", truncatingFormat("%q", expectedText), truncatingFormat("%s", chain), ), msgAndArgs...) } @@ -2141,24 +2246,70 @@ func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{ return true } - chain := buildErrorChainString(err) + expectedType := reflect.TypeOf(target).Elem().String() + if err == nil { + return Fail(t, fmt.Sprintf("An error is expected but got nil.\n"+ + "expected: %s", expectedType), msgAndArgs...) + } + + chain := buildErrorChainString(err, true) return Fail(t, fmt.Sprintf("Should be in error chain:\n"+ - "expected: %q\n"+ - "in chain: %s", target, chain, + "expected: %s\n"+ + "in chain: %s", expectedType, truncatingFormat("%s", chain), ), msgAndArgs...) } -func buildErrorChainString(err error) string { +// NotErrorAs asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func NotErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if !errors.As(err, target) { + return true + } + + chain := buildErrorChainString(err, true) + + return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ + "found: %s\n"+ + "in chain: %s", reflect.TypeOf(target).Elem().String(), truncatingFormat("%s", chain), + ), msgAndArgs...) +} + +func unwrapAll(err error) (errs []error) { + errs = append(errs, err) + switch x := err.(type) { + case interface{ Unwrap() error }: + err = x.Unwrap() + if err == nil { + return + } + errs = append(errs, unwrapAll(err)...) + case interface{ Unwrap() []error }: + for _, err := range x.Unwrap() { + errs = append(errs, unwrapAll(err)...) + } + } + return +} + +func buildErrorChainString(err error, withType bool) string { if err == nil { return "" } - e := errors.Unwrap(err) - chain := fmt.Sprintf("%q", err.Error()) - for e != nil { - chain += fmt.Sprintf("\n\t%q", e.Error()) - e = errors.Unwrap(e) + var chain string + errs := unwrapAll(err) + for i := range errs { + if i != 0 { + chain += "\n\t" + } + chain += fmt.Sprintf("%q", errs[i].Error()) + if withType { + chain += fmt.Sprintf(" (%T)", errs[i]) + } } return chain } diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 064b92f4a..4975f5e41 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -91,17 +91,17 @@ type AssertionTesterInterface interface { } // AssertionTesterConformingObject is an object that conforms to the AssertionTesterInterface interface -type AssertionTesterConformingObject struct { -} +type AssertionTesterConformingObject struct{} func (a *AssertionTesterConformingObject) TestMethod() { } // AssertionTesterNonConformingObject is an object that does not conform to the AssertionTesterInterface interface -type AssertionTesterNonConformingObject struct { -} +type AssertionTesterNonConformingObject struct{} func TestObjectsAreEqual(t *testing.T) { + t.Parallel() + cases := []struct { expected interface{} actual interface{} @@ -132,12 +132,13 @@ func TestObjectsAreEqual(t *testing.T) { if res != c.result { t.Errorf("ObjectsAreEqual(%#v, %#v) should return %#v", c.expected, c.actual, c.result) } - }) } } func TestObjectsAreEqualValues(t *testing.T) { + t.Parallel() + now := time.Now() cases := []struct { @@ -208,6 +209,7 @@ type S6 struct { } func TestObjectsExportedFieldsAreEqual(t *testing.T) { + t.Parallel() intValue := 1 @@ -277,12 +279,13 @@ func TestObjectsExportedFieldsAreEqual(t *testing.T) { if res != c.result { t.Errorf("ObjectsExportedFieldsAreEqual(%#v, %#v) should return %#v", c.expected, c.actual, c.result) } - }) } } func TestCopyExportedFields(t *testing.T) { + t.Parallel() + intValue := 1 cases := []struct { @@ -328,11 +331,15 @@ func TestCopyExportedFields(t *testing.T) { }}, }, { - input: S4{[]*Nested{ - {1, 2}}, + input: S4{ + []*Nested{ + {1, 2}, + }, }, - expected: S4{[]*Nested{ - {1, nil}}, + expected: S4{ + []*Nested{ + {1, nil}, + }, }, }, { @@ -366,6 +373,8 @@ func TestCopyExportedFields(t *testing.T) { } func TestEqualExportedValues(t *testing.T) { + t.Parallel() + cases := []struct { value1 interface{} value2 interface{} @@ -449,6 +458,56 @@ func TestEqualExportedValues(t *testing.T) { + Exported: (int) 1, notExported: (interface {}) `, }, + { + value1: []int{1, 2}, + value2: []int{1, 2}, + expectedEqual: true, + }, + { + value1: []int{1, 2}, + value2: []int{1, 3}, + expectedEqual: false, + expectedFail: ` + Diff: + --- Expected + +++ Actual + @@ -2,3 +2,3 @@ + (int) 1, + - (int) 2 + + (int) 3 + }`, + }, + { + value1: []*Nested{ + {1, 2}, + {3, 4}, + }, + value2: []*Nested{ + {1, "a"}, + {3, "b"}, + }, + expectedEqual: true, + }, + { + value1: []*Nested{ + {1, 2}, + {3, 4}, + }, + value2: []*Nested{ + {1, "a"}, + {2, "b"}, + }, + expectedEqual: false, + expectedFail: ` + Diff: + --- Expected + +++ Actual + @@ -6,3 +6,3 @@ + (*assert.Nested)({ + - Exported: (int) 3, + + Exported: (int) 2, + notExported: (interface {}) `, + }, } for _, c := range cases { @@ -466,10 +525,10 @@ func TestEqualExportedValues(t *testing.T) { } }) } - } func TestImplements(t *testing.T) { + t.Parallel() mockT := new(testing.T) @@ -482,10 +541,10 @@ func TestImplements(t *testing.T) { if Implements(mockT, (*AssertionTesterInterface)(nil), nil) { t.Error("Implements method should return false: nil does not implement AssertionTesterInterface") } - } func TestNotImplements(t *testing.T) { + t.Parallel() mockT := new(testing.T) @@ -498,10 +557,10 @@ func TestNotImplements(t *testing.T) { if NotImplements(mockT, (*AssertionTesterInterface)(nil), nil) { t.Error("NotImplements method should return false: nil can't be checked to be implementing AssertionTesterInterface or not") } - } func TestIsType(t *testing.T) { + t.Parallel() mockT := new(testing.T) @@ -511,10 +570,24 @@ func TestIsType(t *testing.T) { if IsType(mockT, new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject)) { t.Error("IsType should return false: AssertionTesterConformingObject is not the same type as AssertionTesterNonConformingObject") } +} + +func TestNotIsType(t *testing.T) { + t.Parallel() + + mockT := new(testing.T) + if !IsNotType(mockT, new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject)) { + t.Error("NotIsType should return true: AssertionTesterConformingObject is not the same type as AssertionTesterNonConformingObject") + } + if IsNotType(mockT, new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) { + t.Error("NotIsType should return false: AssertionTesterConformingObject is the same type as AssertionTesterConformingObject") + } } func TestEqual(t *testing.T) { + t.Parallel() + type myType string mockT := new(testing.T) @@ -560,8 +633,9 @@ func ptr(i int) *int { } func TestSame(t *testing.T) { + t.Parallel() - mockT := new(testing.T) + mockT := new(mockTestingT) if Same(mockT, ptr(1), ptr(1)) { t.Error("Same should return false") @@ -576,9 +650,26 @@ func TestSame(t *testing.T) { if !Same(mockT, p, p) { t.Error("Same should return true") } + + t.Run("same object, different type", func(t *testing.T) { + type s struct { + i int + } + type sPtr *s + ps := &s{1} + dps := sPtr(ps) + if Same(mockT, dps, ps) { + t.Error("Same should return false") + } + expPat := + `expected: &assert.s\{i:1\} \(assert.sPtr\)\((0x[a-f0-9]+)\)\s*\n` + + `\s+actual : &assert.s\{i:1\} \(\*assert.s\)\((0x[a-f0-9]+)\)` + Regexp(t, regexp.MustCompile(expPat), mockT.errorString()) + }) } func TestNotSame(t *testing.T) { + t.Parallel() mockT := new(testing.T) @@ -598,6 +689,8 @@ func TestNotSame(t *testing.T) { } func Test_samePointers(t *testing.T) { + t.Parallel() + p := ptr(2) type args struct { @@ -605,39 +698,59 @@ func Test_samePointers(t *testing.T) { second interface{} } tests := []struct { - name string - args args - assertion BoolAssertionFunc + name string + args args + same BoolAssertionFunc + ok BoolAssertionFunc }{ { - name: "1 != 2", - args: args{first: 1, second: 2}, - assertion: False, + name: "1 != 2", + args: args{first: 1, second: 2}, + same: False, + ok: False, + }, + { + name: "1 != 1 (not same ptr)", + args: args{first: 1, second: 1}, + same: False, + ok: False, + }, + { + name: "ptr(1) == ptr(1)", + args: args{first: p, second: p}, + same: True, + ok: True, }, { - name: "1 != 1 (not same ptr)", - args: args{first: 1, second: 1}, - assertion: False, + name: "int(1) != float32(1)", + args: args{first: int(1), second: float32(1)}, + same: False, + ok: False, }, { - name: "ptr(1) == ptr(1)", - args: args{first: p, second: p}, - assertion: True, + name: "array != slice", + args: args{first: [2]int{1, 2}, second: []int{1, 2}}, + same: False, + ok: False, }, { - name: "int(1) != float32(1)", - args: args{first: int(1), second: float32(1)}, - assertion: False, + name: "non-pointer vs pointer (1 != ptr(2))", + args: args{first: 1, second: p}, + same: False, + ok: False, }, { - name: "array != slice", - args: args{first: [2]int{1, 2}, second: []int{1, 2}}, - assertion: False, + name: "pointer vs non-pointer (ptr(2) != 1)", + args: args{first: p, second: 1}, + same: False, + ok: False, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - tt.assertion(t, samePointers(tt.args.first, tt.args.second)) + same, ok := samePointers(tt.args.first, tt.args.second) + tt.same(t, same) + tt.ok(t, ok) }) } } @@ -648,6 +761,9 @@ type bufferT struct { buf bytes.Buffer } +// Helper is like [testing.T.Helper] but does nothing. +func (bufferT) Helper() {} + func (t *bufferT) Errorf(format string, args ...interface{}) { // implementation of decorate is copied from testing.T decorate := func(s string) string { @@ -685,6 +801,8 @@ func (t *bufferT) Errorf(format string, args ...interface{}) { } func TestStringEqual(t *testing.T) { + t.Parallel() + for i, currCase := range []struct { equalWant string equalGot string @@ -700,6 +818,8 @@ func TestStringEqual(t *testing.T) { } func TestEqualFormatting(t *testing.T) { + t.Parallel() + for i, currCase := range []struct { equalWant string equalGot string @@ -718,6 +838,8 @@ func TestEqualFormatting(t *testing.T) { } func TestFormatUnequalValues(t *testing.T) { + t.Parallel() + expected, actual := formatUnequalValues("foo", "bar") Equal(t, `"foo"`, expected, "value should not include type") Equal(t, `"bar"`, actual, "value should not include type") @@ -744,6 +866,7 @@ func TestFormatUnequalValues(t *testing.T) { } func TestNotNil(t *testing.T) { + t.Parallel() mockT := new(testing.T) @@ -756,10 +879,10 @@ func TestNotNil(t *testing.T) { if NotNil(mockT, (*struct{})(nil)) { t.Error("NotNil should return false: object is (*struct{})(nil)") } - } func TestNil(t *testing.T) { + t.Parallel() mockT := new(testing.T) @@ -772,10 +895,10 @@ func TestNil(t *testing.T) { if Nil(mockT, new(AssertionTesterConformingObject)) { t.Error("Nil should return false: object is not nil") } - } func TestTrue(t *testing.T) { + t.Parallel() mockT := new(testing.T) @@ -785,10 +908,10 @@ func TestTrue(t *testing.T) { if True(mockT, false) { t.Error("True should return false") } - } func TestFalse(t *testing.T) { + t.Parallel() mockT := new(testing.T) @@ -798,11 +921,10 @@ func TestFalse(t *testing.T) { if False(mockT, true) { t.Error("False should return false") } - } func TestExactly(t *testing.T) { - + t.Parallel() mockT := new(testing.T) a := float32(1) @@ -833,7 +955,7 @@ func TestExactly(t *testing.T) { } func TestNotEqual(t *testing.T) { - + t.Parallel() mockT := new(testing.T) cases := []struct { @@ -872,13 +994,15 @@ func TestNotEqual(t *testing.T) { } } -func TestNotEqualValues(t *testing.T) { +func TestEqualValuesAndNotEqualValues(t *testing.T) { + t.Parallel() + mockT := new(testing.T) cases := []struct { - expected interface{} - actual interface{} - result bool + expected interface{} + actual interface{} + notEqualResult bool // result for NotEqualValues }{ // cases that are expected not to match {"Hello World", "Hello World!", true}, @@ -905,17 +1029,29 @@ func TestNotEqualValues(t *testing.T) { } for _, c := range cases { + // Test NotEqualValues t.Run(fmt.Sprintf("NotEqualValues(%#v, %#v)", c.expected, c.actual), func(t *testing.T) { res := NotEqualValues(mockT, c.expected, c.actual) - if res != c.result { - t.Errorf("NotEqualValues(%#v, %#v) should return %#v", c.expected, c.actual, c.result) + if res != c.notEqualResult { + t.Errorf("NotEqualValues(%#v, %#v) should return %#v", c.expected, c.actual, c.notEqualResult) + } + }) + + // Test EqualValues (inverse of NotEqualValues) + t.Run(fmt.Sprintf("EqualValues(%#v, %#v)", c.expected, c.actual), func(t *testing.T) { + expectedEqualResult := !c.notEqualResult // EqualValues should return opposite of NotEqualValues + res := EqualValues(mockT, c.expected, c.actual) + + if res != expectedEqualResult { + t.Errorf("EqualValues(%#v, %#v) should return %#v", c.expected, c.actual, expectedEqualResult) } }) } } func TestContainsNotContains(t *testing.T) { + t.Parallel() type A struct { Name, Value string @@ -980,6 +1116,8 @@ func TestContainsNotContains(t *testing.T) { } func TestContainsNotContainsFailMessage(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) type nonContainer struct { @@ -1036,6 +1174,8 @@ func TestContainsNotContainsFailMessage(t *testing.T) { } func TestContainsNotContainsOnNilValue(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) Contains(mockT, nil, "key") @@ -1052,6 +1192,8 @@ func TestContainsNotContainsOnNilValue(t *testing.T) { } func TestSubsetNotSubset(t *testing.T) { + t.Parallel() + cases := []struct { list interface{} subset interface{} @@ -1072,6 +1214,7 @@ func TestSubsetNotSubset(t *testing.T) { "a": "x", "b": "y", }, true, `map["a":"x" "b":"y"] is a subset of map["a":"x" "b":"y" "c":"z"]`}, + {[]string{"a", "b", "c"}, map[string]int{"a": 1, "c": 3}, true, `map["a":'\x01' "c":'\x03'] is a subset of ["a" "b" "c"]`}, // cases that are expected not to contain {[]string{"hello", "world"}, []string{"hello", "testify"}, false, `[]string{"hello", "world"} does not contain "testify"`}, @@ -1093,11 +1236,11 @@ func TestSubsetNotSubset(t *testing.T) { "b": "y", "c": "z", }, false, `map[string]string{"a":"x", "b":"y"} does not contain map[string]string{"a":"x", "b":"y", "c":"z"}`}, + {[]string{"a", "b", "c"}, map[string]int{"c": 3, "d": 4}, false, `[]string{"a", "b", "c"} does not contain "d"`}, } for _, c := range cases { t.Run("SubSet: "+c.message, func(t *testing.T) { - mockT := new(mockTestingT) res := Subset(mockT, c.list, c.subset) @@ -1136,6 +1279,8 @@ func TestSubsetNotSubset(t *testing.T) { } func TestNotSubsetNil(t *testing.T) { + t.Parallel() + mockT := new(testing.T) NotSubset(mockT, []string{"foo"}, nil) if !mockT.Failed() { @@ -1144,6 +1289,7 @@ func TestNotSubsetNil(t *testing.T) { } func Test_containsElement(t *testing.T) { + t.Parallel() list1 := []string{"Foo", "Bar"} list2 := []int{1, 2} @@ -1195,6 +1341,8 @@ func Test_containsElement(t *testing.T) { } func TestElementsMatch(t *testing.T) { + t.Parallel() + mockT := new(testing.T) cases := []struct { @@ -1236,6 +1384,8 @@ func TestElementsMatch(t *testing.T) { } func TestDiffLists(t *testing.T) { + t.Parallel() + tests := []struct { name string listA interface{} @@ -1320,6 +1470,8 @@ func TestDiffLists(t *testing.T) { } func TestNotElementsMatch(t *testing.T) { + t.Parallel() + mockT := new(testing.T) cases := []struct { @@ -1327,7 +1479,7 @@ func TestNotElementsMatch(t *testing.T) { actual interface{} result bool }{ - // not mathing + // not matching {[]int{1}, []int{}, true}, {[]int{}, []int{2}, true}, {[]int{1}, []int{2}, true}, @@ -1366,6 +1518,8 @@ func TestNotElementsMatch(t *testing.T) { } func TestCondition(t *testing.T) { + t.Parallel() + mockT := new(testing.T) if !Condition(mockT, func() bool { return true }, "Truth") { @@ -1375,10 +1529,10 @@ func TestCondition(t *testing.T) { if Condition(mockT, func() bool { return false }, "Lie") { t.Error("Condition should return false") } - } func TestDidPanic(t *testing.T) { + t.Parallel() const panicMsg = "Panic!" @@ -1398,10 +1552,10 @@ func TestDidPanic(t *testing.T) { }); funcDidPanic { t.Error("didPanic should return false") } - } func TestPanics(t *testing.T) { + t.Parallel() mockT := new(testing.T) @@ -1415,10 +1569,10 @@ func TestPanics(t *testing.T) { }) { t.Error("Panics should return false") } - } func TestPanicsWithValue(t *testing.T) { + t.Parallel() mockT := new(testing.T) @@ -1447,34 +1601,49 @@ func TestPanicsWithValue(t *testing.T) { } func TestPanicsWithError(t *testing.T) { + t.Parallel() - mockT := new(testing.T) - - if !PanicsWithError(mockT, "panic", func() { + mockT := new(captureTestingT) + succeeded := PanicsWithError(mockT, "panic", func() { panic(errors.New("panic")) - }) { - t.Error("PanicsWithError should return true") - } + }) + mockT.checkResultAndErrMsg(t, true, succeeded, "") - if PanicsWithError(mockT, "Panic!", func() { - }) { - t.Error("PanicsWithError should return false") - } + succeeded = PanicsWithError(mockT, "Panic!", func() {}) + Equal(t, false, succeeded, "PanicsWithError should return false") + Contains(t, mockT.msg, "Panic value:\t") - if PanicsWithError(mockT, "at the disco", func() { - panic(errors.New("panic")) - }) { - t.Error("PanicsWithError should return false") - } + succeeded = PanicsWithError(mockT, "expected panic err msg", func() { + panic(errors.New("actual panic err msg")) + }) + Equal(t, false, succeeded, "PanicsWithError should return false") + Contains(t, mockT.msg, `Error message: "actual panic err msg"`) - if PanicsWithError(mockT, "Panic!", func() { - panic("panic") - }) { - t.Error("PanicsWithError should return false") - } + succeeded = PanicsWithError(mockT, "expected panic err msg", func() { + panic(&PanicsWithErrorWrapper{"wrapped", errors.New("actual panic err msg")}) + }) + Equal(t, false, succeeded, "PanicsWithError should return false") + Contains(t, mockT.msg, `Error message: "wrapped: actual panic err msg"`) + + succeeded = PanicsWithError(mockT, "expected panic msg", func() { + panic("actual panic msg") + }) + Equal(t, false, succeeded, "PanicsWithError should return false") + Contains(t, mockT.msg, `Panic value: "actual panic msg"`) + NotContains(t, mockT.msg, "Error message:", "PanicsWithError should not report error message if not due an error") +} + +type PanicsWithErrorWrapper struct { + Prefix string + Err error +} + +func (e PanicsWithErrorWrapper) Error() string { + return e.Prefix + ": " + e.Err.Error() } func TestNotPanics(t *testing.T) { + t.Parallel() mockT := new(testing.T) @@ -1488,10 +1657,10 @@ func TestNotPanics(t *testing.T) { }) { t.Error("NotPanics should return false") } - } func TestNoError(t *testing.T) { + t.Parallel() mockT := new(testing.T) @@ -1523,6 +1692,7 @@ type customError struct{} func (*customError) Error() string { return "fail" } func TestError(t *testing.T) { + t.Parallel() mockT := new(testing.T) @@ -1553,6 +1723,8 @@ func TestError(t *testing.T) { } func TestEqualError(t *testing.T) { + t.Parallel() + mockT := new(testing.T) // start with a nil error @@ -1569,6 +1741,8 @@ func TestEqualError(t *testing.T) { } func TestErrorContains(t *testing.T) { + t.Parallel() + mockT := new(testing.T) // start with a nil error @@ -1587,33 +1761,143 @@ func TestErrorContains(t *testing.T) { } func Test_isEmpty(t *testing.T) { + t.Parallel() chWithValue := make(chan struct{}, 1) chWithValue <- struct{}{} True(t, isEmpty("")) True(t, isEmpty(nil)) + True(t, isEmpty(error(nil))) + True(t, isEmpty((*int)(nil))) + True(t, isEmpty((*string)(nil))) + True(t, isEmpty(new(string))) True(t, isEmpty([]string{})) + True(t, isEmpty([]string(nil))) + True(t, isEmpty([]byte(nil))) + True(t, isEmpty([]byte{})) + True(t, isEmpty([]byte(""))) + True(t, isEmpty([]bool(nil))) + True(t, isEmpty([]bool{})) + True(t, isEmpty([]interface{}(nil))) + True(t, isEmpty([]interface{}{})) + True(t, isEmpty(struct{}{})) + True(t, isEmpty(&struct{}{})) + True(t, isEmpty(struct{ A int }{A: 0})) + True(t, isEmpty(struct{ a int }{a: 0})) + True(t, isEmpty(struct { + a int + B int + }{a: 0, B: 0})) True(t, isEmpty(0)) + True(t, isEmpty(int(0))) + True(t, isEmpty(int8(0))) + True(t, isEmpty(int16(0))) + True(t, isEmpty(uint16(0))) True(t, isEmpty(int32(0))) + True(t, isEmpty(uint32(0))) True(t, isEmpty(int64(0))) + True(t, isEmpty(uint64(0))) + True(t, isEmpty('\u0000')) // rune => int32 + True(t, isEmpty(float32(0))) + True(t, isEmpty(float64(0))) + True(t, isEmpty(0i)) // complex + True(t, isEmpty(0.0i)) // complex True(t, isEmpty(false)) + True(t, isEmpty(new(bool))) True(t, isEmpty(map[string]string{})) + True(t, isEmpty(map[string]string(nil))) True(t, isEmpty(new(time.Time))) True(t, isEmpty(time.Time{})) True(t, isEmpty(make(chan struct{}))) - True(t, isEmpty([1]int{})) + True(t, isEmpty(chan struct{}(nil))) + True(t, isEmpty(chan<- struct{}(nil))) + True(t, isEmpty(make(chan struct{}))) + True(t, isEmpty(make(chan<- struct{}))) + True(t, isEmpty(make(chan struct{}, 1))) + True(t, isEmpty(make(chan<- struct{}, 1))) + True(t, isEmpty([1]int{0})) + True(t, isEmpty([2]int{0, 0})) + True(t, isEmpty([8]int{})) + True(t, isEmpty([...]int{7: 0})) + True(t, isEmpty([...]bool{false, false})) + True(t, isEmpty(errors.New(""))) // BEWARE + True(t, isEmpty([]error{})) + True(t, isEmpty([]error(nil))) + True(t, isEmpty(&[1]int{0})) + True(t, isEmpty(&[2]int{0, 0})) False(t, isEmpty("something")) False(t, isEmpty(errors.New("something"))) False(t, isEmpty([]string{"something"})) False(t, isEmpty(1)) + False(t, isEmpty(int(1))) + False(t, isEmpty(uint(1))) + False(t, isEmpty(byte(1))) + False(t, isEmpty(int8(1))) + False(t, isEmpty(uint8(1))) + False(t, isEmpty(int16(1))) + False(t, isEmpty(uint16(1))) + False(t, isEmpty(int32(1))) + False(t, isEmpty(uint32(1))) + False(t, isEmpty(int64(1))) + False(t, isEmpty(uint64(1))) + False(t, isEmpty('A')) // rune => int32 False(t, isEmpty(true)) + False(t, isEmpty(1.0)) + False(t, isEmpty(1i)) // complex + False(t, isEmpty([]byte{0})) // elements values are ignored for slices + False(t, isEmpty([]byte{0, 0})) // elements values are ignored for slices + False(t, isEmpty([]string{""})) // elements values are ignored for slices + False(t, isEmpty([]string{"a"})) // elements values are ignored for slices + False(t, isEmpty([]bool{false})) // elements values are ignored for slices + False(t, isEmpty([]bool{true})) // elements values are ignored for slices + False(t, isEmpty([]error{errors.New("xxx")})) + False(t, isEmpty([]error{nil})) // BEWARE + False(t, isEmpty([]error{errors.New("")})) // BEWARE False(t, isEmpty(map[string]string{"Hello": "World"})) + False(t, isEmpty(map[string]string{"": ""})) + False(t, isEmpty(map[string]string{"foo": ""})) + False(t, isEmpty(map[string]string{"": "foo"})) False(t, isEmpty(chWithValue)) + False(t, isEmpty([1]bool{true})) + False(t, isEmpty([2]bool{false, true})) + False(t, isEmpty([...]bool{10: true})) + False(t, isEmpty([]int{0})) + False(t, isEmpty([]int{42})) False(t, isEmpty([1]int{42})) + False(t, isEmpty([2]int{0, 42})) + False(t, isEmpty(&[1]int{42})) + False(t, isEmpty(&[2]int{0, 42})) + False(t, isEmpty([1]*int{new(int)})) // array elements must be the zero value, not any Empty value + False(t, isEmpty(struct{ A int }{A: 42})) + False(t, isEmpty(struct{ a int }{a: 42})) + False(t, isEmpty(struct{ a *int }{a: new(int)})) // fields must be the zero value, not any Empty value + False(t, isEmpty(struct{ a []int }{a: []int{}})) // fields must be the zero value, not any Empty value + False(t, isEmpty(struct { + a int + B int + }{a: 0, B: 42})) + False(t, isEmpty(struct { + a int + B int + }{a: 42, B: 0})) +} + +func Benchmark_isEmpty(b *testing.B) { + b.ReportAllocs() + + v := new(int) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + isEmpty("") + isEmpty(42) + isEmpty(v) + } } func TestEmpty(t *testing.T) { + t.Parallel() mockT := new(testing.T) chWithValue := make(chan struct{}, 1) @@ -1656,9 +1940,146 @@ func TestEmpty(t *testing.T) { False(t, Empty(mockT, TString("abc")), "non-empty aliased string is empty") False(t, Empty(mockT, xP), "ptr to non-nil value is not empty") False(t, Empty(mockT, [1]int{42}), "array is not state") + + // error messages validation + tests := []struct { + name string + value interface{} + expectedResult bool + expectedErrMsg string + }{ + { + name: "Non Empty string is not empty", + value: "something", + expectedResult: false, + expectedErrMsg: "Should be empty, but was something\n", + }, + { + name: "Non nil object is not empty", + value: errors.New("something"), + expectedResult: false, + expectedErrMsg: "Should be empty, but was something\n", + }, + { + name: "Non empty string array is not empty", + value: []string{"something"}, + expectedResult: false, + expectedErrMsg: "Should be empty, but was [something]\n", + }, + { + name: "Non-zero int value is not empty", + value: 1, + expectedResult: false, + expectedErrMsg: "Should be empty, but was 1\n", + }, + { + name: "True value is not empty", + value: true, + expectedResult: false, + expectedErrMsg: "Should be empty, but was true\n", + }, + { + name: "Channel with values is not empty", + value: chWithValue, + expectedResult: false, + expectedErrMsg: fmt.Sprintf("Should be empty, but was %v\n", chWithValue), + }, + { + name: "struct with initialized values is empty", + value: TStruct{x: 1}, + expectedResult: false, + expectedErrMsg: "Should be empty, but was {1}\n", + }, + { + name: "non-empty aliased string is empty", + value: TString("abc"), + expectedResult: false, + expectedErrMsg: "Should be empty, but was abc\n", + }, + { + name: "ptr to non-nil value is not empty", + value: xP, + expectedResult: false, + expectedErrMsg: fmt.Sprintf("Should be empty, but was %p\n", xP), + }, + { + name: "array is not state", + value: [1]int{42}, + expectedResult: false, + expectedErrMsg: "Should be empty, but was [42]\n", + }, + + // Here are some edge cases + { + name: "string with only spaces is not empty", + value: " ", + expectedResult: false, + expectedErrMsg: "Should be empty, but was \n", // TODO FIX THIS strange error message + }, + { + name: "string with a line feed is not empty", + value: "\n", + expectedResult: false, + // TODO This is the exact same error message as for an empty string + expectedErrMsg: "Should be empty, but was \n", // TODO FIX THIS strange error message + }, + { + name: "string with only tabulation and lines feed is not empty", + value: "\n\t\n", + expectedResult: false, + // TODO The line feeds and tab are not helping to spot what is expected + expectedErrMsg: "" + // this syntax is used to show how errors are reported. + "Should be empty, but was \n" + + "\t\n", + }, + { + name: "string with trailing lines feed is not empty", + value: "foo\n\n", + expectedResult: false, + // TODO it's not clear if one or two lines feed are expected + expectedErrMsg: "Should be empty, but was foo\n\n", + }, + { + name: "string with leading and trailing tabulation and lines feed is not empty", + value: "\n\nfoo\t\n\t\n", + expectedResult: false, + // TODO The line feeds and tab are not helping to figure what is expected + expectedErrMsg: "" + + "Should be empty, but was \n" + + "\n" + + "foo\t\n" + + "\t\n", + }, + + { + name: "non-printable character is not empty", + value: "\u00a0", // NO-BREAK SPACE UNICODE CHARACTER + expectedResult: false, + // TODO here you cannot figure out what is expected + expectedErrMsg: "Should be empty, but was \u00a0\n", + }, + + // Here we are testing there is no error message on success + { + name: "Empty string is empty", + value: "", + expectedResult: true, + expectedErrMsg: "", + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + mockCT := new(captureTestingT) + res := Empty(mockCT, tt.value) + mockCT.checkResultAndErrMsg(t, res, tt.expectedResult, tt.expectedErrMsg) + }) + } } func TestNotEmpty(t *testing.T) { + t.Parallel() mockT := new(testing.T) chWithValue := make(chan struct{}, 1) @@ -1679,9 +2100,73 @@ func TestNotEmpty(t *testing.T) { True(t, NotEmpty(mockT, true), "True value is not empty") True(t, NotEmpty(mockT, chWithValue), "Channel with values is not empty") True(t, NotEmpty(mockT, [1]int{42}), "array is not state") + + // error messages validation + tests := []struct { + name string + value interface{} + expectedResult bool + expectedErrMsg string + }{ + { + name: "Empty string is empty", + value: "", + expectedResult: false, + expectedErrMsg: `Should NOT be empty, but was ` + "\n", // TODO FIX THIS strange error message + }, + { + name: "Nil is empty", + value: nil, + expectedResult: false, + expectedErrMsg: "Should NOT be empty, but was \n", + }, + { + name: "Empty string array is empty", + value: []string{}, + expectedResult: false, + expectedErrMsg: "Should NOT be empty, but was []\n", + }, + { + name: "Zero int value is empty", + value: 0, + expectedResult: false, + expectedErrMsg: "Should NOT be empty, but was 0\n", + }, + { + name: "False value is empty", + value: false, + expectedResult: false, + expectedErrMsg: "Should NOT be empty, but was false\n", + }, + { + name: "array is state", + value: [1]int{}, + expectedResult: false, + expectedErrMsg: "Should NOT be empty, but was [0]\n", + }, + + // Here we are testing there is no error message on success + { + name: "Non Empty string is not empty", + value: "something", + expectedResult: true, + expectedErrMsg: "", + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + mockCT := new(captureTestingT) + res := NotEmpty(mockCT, tt.value) + mockCT.checkResultAndErrMsg(t, tt.expectedResult, res, tt.expectedErrMsg) + }) + } } func Test_getLen(t *testing.T) { + t.Parallel() + falseCases := []interface{}{ nil, 0, @@ -1727,6 +2212,8 @@ func Test_getLen(t *testing.T) { } func TestLen(t *testing.T) { + t.Parallel() + mockT := new(testing.T) False(t, Len(mockT, nil, 0), "nil does not have length") @@ -1773,6 +2260,7 @@ func TestLen(t *testing.T) { } func TestWithinDuration(t *testing.T) { + t.Parallel() mockT := new(testing.T) a := time.Now() @@ -1792,6 +2280,7 @@ func TestWithinDuration(t *testing.T) { } func TestWithinRange(t *testing.T) { + t.Parallel() mockT := new(testing.T) n := time.Now() @@ -1811,6 +2300,8 @@ func TestWithinRange(t *testing.T) { } func TestInDelta(t *testing.T) { + t.Parallel() + mockT := new(testing.T) True(t, InDelta(mockT, 1.001, 1, 0.01), "|1.001 - 1| <= 0.01") @@ -1849,6 +2340,8 @@ func TestInDelta(t *testing.T) { } func TestInDeltaSlice(t *testing.T) { + t.Parallel() + mockT := new(testing.T) True(t, InDeltaSlice(mockT, @@ -1870,6 +2363,8 @@ func TestInDeltaSlice(t *testing.T) { } func TestInDeltaMapValues(t *testing.T) { + t.Parallel() + mockT := new(testing.T) for _, tc := range []struct { @@ -1948,6 +2443,8 @@ func TestInDeltaMapValues(t *testing.T) { } func TestInEpsilon(t *testing.T) { + t.Parallel() + mockT := new(testing.T) cases := []struct { @@ -1986,15 +2483,24 @@ func TestInEpsilon(t *testing.T) { {math.NaN(), 0, 1}, {0, math.NaN(), 1}, {0, 0, math.NaN()}, + {math.Inf(1), 1, 1}, + {math.Inf(-1), 1, 1}, + {1, math.Inf(1), 1}, + {1, math.Inf(-1), 1}, + {math.Inf(1), math.Inf(1), 1}, + {math.Inf(1), math.Inf(-1), 1}, + {math.Inf(-1), math.Inf(1), 1}, + {math.Inf(-1), math.Inf(-1), 1}, } for _, tc := range cases { False(t, InEpsilon(mockT, tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon)) } - } func TestInEpsilonSlice(t *testing.T) { + t.Parallel() + mockT := new(testing.T) True(t, InEpsilonSlice(mockT, @@ -2011,6 +2517,8 @@ func TestInEpsilonSlice(t *testing.T) { } func TestRegexp(t *testing.T) { + t.Parallel() + mockT := new(testing.T) cases := []struct { @@ -2040,7 +2548,7 @@ func TestRegexp(t *testing.T) { } for _, tc := range cases { - False(t, Regexp(mockT, tc.rx, tc.str), "Expected \"%s\" to not match \"%s\"", tc.rx, tc.str) + False(t, Regexp(mockT, tc.rx, tc.str), "Expected %q to not match %q", tc.rx, tc.str) False(t, Regexp(mockT, regexp.MustCompile(tc.rx), tc.str)) False(t, Regexp(mockT, regexp.MustCompile(tc.rx), []byte(tc.str))) True(t, NotRegexp(mockT, tc.rx, tc.str)) @@ -2064,36 +2572,44 @@ func testAutogeneratedFunction() { } func TestCallerInfoWithAutogeneratedFunctions(t *testing.T) { + t.Parallel() + NotPanics(t, func() { testAutogeneratedFunction() }) } func TestZero(t *testing.T) { + t.Parallel() + mockT := new(testing.T) for _, test := range zeros { - True(t, Zero(mockT, test, "%#v is not the %v zero value", test, reflect.TypeOf(test))) + True(t, Zero(mockT, test, "%#v is not the %T zero value", test, test)) } for _, test := range nonZeros { - False(t, Zero(mockT, test, "%#v is not the %v zero value", test, reflect.TypeOf(test))) + False(t, Zero(mockT, test, "%#v is not the %T zero value", test, test)) } } func TestNotZero(t *testing.T) { + t.Parallel() + mockT := new(testing.T) for _, test := range zeros { - False(t, NotZero(mockT, test, "%#v is not the %v zero value", test, reflect.TypeOf(test))) + False(t, NotZero(mockT, test, "%#v is not the %T zero value", test, test)) } for _, test := range nonZeros { - True(t, NotZero(mockT, test, "%#v is not the %v zero value", test, reflect.TypeOf(test))) + True(t, NotZero(mockT, test, "%#v is not the %T zero value", test, test)) } } func TestFileExists(t *testing.T) { + t.Parallel() + mockT := new(testing.T) True(t, FileExists(mockT, "assertions.go")) @@ -2103,31 +2619,18 @@ func TestFileExists(t *testing.T) { mockT = new(testing.T) False(t, FileExists(mockT, "../_codegen")) - var tempFiles []string - - link, err := getTempSymlinkPath("assertions.go") - if err != nil { - t.Fatal("could not create temp symlink, err:", err) - } - tempFiles = append(tempFiles, link) + link := getTempSymlinkPath(t, "assertions.go") mockT = new(testing.T) True(t, FileExists(mockT, link)) - link, err = getTempSymlinkPath("non_existent_file") - if err != nil { - t.Fatal("could not create temp symlink, err:", err) - } - tempFiles = append(tempFiles, link) + link = getTempSymlinkPath(t, "non_existent_file") mockT = new(testing.T) True(t, FileExists(mockT, link)) - - errs := cleanUpTempFiles(tempFiles) - if len(errs) > 0 { - t.Fatal("could not clean up temporary files") - } } func TestNoFileExists(t *testing.T) { + t.Parallel() + mockT := new(testing.T) False(t, NoFileExists(mockT, "assertions.go")) @@ -2137,48 +2640,29 @@ func TestNoFileExists(t *testing.T) { mockT = new(testing.T) True(t, NoFileExists(mockT, "../_codegen")) - var tempFiles []string - - link, err := getTempSymlinkPath("assertions.go") - if err != nil { - t.Fatal("could not create temp symlink, err:", err) - } - tempFiles = append(tempFiles, link) + link := getTempSymlinkPath(t, "assertions.go") mockT = new(testing.T) False(t, NoFileExists(mockT, link)) - link, err = getTempSymlinkPath("non_existent_file") - if err != nil { - t.Fatal("could not create temp symlink, err:", err) - } - tempFiles = append(tempFiles, link) + link = getTempSymlinkPath(t, "non_existent_file") mockT = new(testing.T) False(t, NoFileExists(mockT, link)) - - errs := cleanUpTempFiles(tempFiles) - if len(errs) > 0 { - t.Fatal("could not clean up temporary files") - } } -func getTempSymlinkPath(file string) (string, error) { - link := file + "_symlink" - err := os.Symlink(file, link) - return link, err -} +func getTempSymlinkPath(t *testing.T, file string) string { + t.Helper() -func cleanUpTempFiles(paths []string) []error { - var res []error - for _, path := range paths { - err := os.Remove(path) - if err != nil { - res = append(res, err) - } + tempDir := t.TempDir() + link := filepath.Join(tempDir, file+"_symlink") + if err := os.Symlink(file, link); err != nil { + t.Fatalf("could not create temp symlink %q pointing to %q: %v", link, file, err) } - return res + return link } func TestDirExists(t *testing.T) { + t.Parallel() + mockT := new(testing.T) False(t, DirExists(mockT, "assertions.go")) @@ -2188,31 +2672,18 @@ func TestDirExists(t *testing.T) { mockT = new(testing.T) True(t, DirExists(mockT, "../_codegen")) - var tempFiles []string - - link, err := getTempSymlinkPath("assertions.go") - if err != nil { - t.Fatal("could not create temp symlink, err:", err) - } - tempFiles = append(tempFiles, link) + link := getTempSymlinkPath(t, "assertions.go") mockT = new(testing.T) False(t, DirExists(mockT, link)) - link, err = getTempSymlinkPath("non_existent_dir") - if err != nil { - t.Fatal("could not create temp symlink, err:", err) - } - tempFiles = append(tempFiles, link) + link = getTempSymlinkPath(t, "non_existent_dir") mockT = new(testing.T) False(t, DirExists(mockT, link)) - - errs := cleanUpTempFiles(tempFiles) - if len(errs) > 0 { - t.Fatal("could not clean up temporary files") - } } func TestNoDirExists(t *testing.T) { + t.Parallel() + mockT := new(testing.T) True(t, NoDirExists(mockT, "assertions.go")) @@ -2222,92 +2693,103 @@ func TestNoDirExists(t *testing.T) { mockT = new(testing.T) False(t, NoDirExists(mockT, "../_codegen")) - var tempFiles []string - - link, err := getTempSymlinkPath("assertions.go") - if err != nil { - t.Fatal("could not create temp symlink, err:", err) - } - tempFiles = append(tempFiles, link) + link := getTempSymlinkPath(t, "assertions.go") mockT = new(testing.T) True(t, NoDirExists(mockT, link)) - link, err = getTempSymlinkPath("non_existent_dir") - if err != nil { - t.Fatal("could not create temp symlink, err:", err) - } - tempFiles = append(tempFiles, link) + link = getTempSymlinkPath(t, "non_existent_dir") mockT = new(testing.T) True(t, NoDirExists(mockT, link)) - - errs := cleanUpTempFiles(tempFiles) - if len(errs) > 0 { - t.Fatal("could not clean up temporary files") - } } func TestJSONEq_EqualSONString(t *testing.T) { + t.Parallel() + mockT := new(testing.T) True(t, JSONEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`)) } func TestJSONEq_EquivalentButNotEqual(t *testing.T) { + t.Parallel() + mockT := new(testing.T) True(t, JSONEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) } func TestJSONEq_HashOfArraysAndHashes(t *testing.T) { + t.Parallel() + mockT := new(testing.T) True(t, JSONEq(mockT, "{\r\n\t\"numeric\": 1.5,\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]],\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\"\r\n}", "{\r\n\t\"numeric\": 1.5,\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\",\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]]\r\n}")) } func TestJSONEq_Array(t *testing.T) { + t.Parallel() + mockT := new(testing.T) True(t, JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`)) } func TestJSONEq_HashAndArrayNotEquivalent(t *testing.T) { + t.Parallel() + mockT := new(testing.T) False(t, JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`)) } func TestJSONEq_HashesNotEquivalent(t *testing.T) { + t.Parallel() + mockT := new(testing.T) False(t, JSONEq(mockT, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) } func TestJSONEq_ActualIsNotJSON(t *testing.T) { + t.Parallel() + mockT := new(testing.T) False(t, JSONEq(mockT, `{"foo": "bar"}`, "Not JSON")) } func TestJSONEq_ExpectedIsNotJSON(t *testing.T) { + t.Parallel() + mockT := new(testing.T) False(t, JSONEq(mockT, "Not JSON", `{"foo": "bar", "hello": "world"}`)) } func TestJSONEq_ExpectedAndActualNotJSON(t *testing.T) { + t.Parallel() + mockT := new(testing.T) False(t, JSONEq(mockT, "Not JSON", "Not JSON")) } func TestJSONEq_ArraysOfDifferentOrder(t *testing.T) { + t.Parallel() + mockT := new(testing.T) False(t, JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`)) } func TestYAMLEq_EqualYAMLString(t *testing.T) { + t.Parallel() + mockT := new(testing.T) True(t, YAMLEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`)) } func TestYAMLEq_EquivalentButNotEqual(t *testing.T) { + t.Parallel() + mockT := new(testing.T) True(t, YAMLEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) } func TestYAMLEq_HashOfArraysAndHashes(t *testing.T) { + t.Parallel() + mockT := new(testing.T) expected := ` numeric: 1.5 @@ -2338,40 +2820,79 @@ array: } func TestYAMLEq_Array(t *testing.T) { + t.Parallel() + mockT := new(testing.T) True(t, YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`)) } func TestYAMLEq_HashAndArrayNotEquivalent(t *testing.T) { + t.Parallel() + mockT := new(testing.T) False(t, YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`)) } func TestYAMLEq_HashesNotEquivalent(t *testing.T) { + t.Parallel() + mockT := new(testing.T) False(t, YAMLEq(mockT, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) } func TestYAMLEq_ActualIsSimpleString(t *testing.T) { + t.Parallel() + mockT := new(testing.T) False(t, YAMLEq(mockT, `{"foo": "bar"}`, "Simple String")) } func TestYAMLEq_ExpectedIsSimpleString(t *testing.T) { + t.Parallel() + mockT := new(testing.T) False(t, YAMLEq(mockT, "Simple String", `{"foo": "bar", "hello": "world"}`)) } func TestYAMLEq_ExpectedAndActualSimpleString(t *testing.T) { + t.Parallel() + mockT := new(testing.T) True(t, YAMLEq(mockT, "Simple String", "Simple String")) } func TestYAMLEq_ArraysOfDifferentOrder(t *testing.T) { + t.Parallel() + mockT := new(testing.T) False(t, YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`)) } +func TestYAMLEq_OnlyFirstDocument(t *testing.T) { + t.Parallel() + + mockT := new(testing.T) + True(t, YAMLEq(mockT, + `--- +doc1: same +--- +doc2: different +`, + `--- +doc1: same +--- +doc2: notsame +`, + )) +} + +func TestYAMLEq_InvalidIdenticalYAML(t *testing.T) { + t.Parallel() + + mockT := new(testing.T) + False(t, YAMLEq(mockT, `}`, `}`)) +} + type diffTestingStruct struct { A string B int @@ -2382,6 +2903,8 @@ func (d *diffTestingStruct) String() string { } func TestDiff(t *testing.T) { + t.Parallel() + expected := ` Diff: @@ -2516,6 +3039,8 @@ Diff: } func TestTimeEqualityErrorFormatting(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) Equal(mockT, time.Second*2, time.Millisecond) @@ -2525,6 +3050,8 @@ func TestTimeEqualityErrorFormatting(t *testing.T) { } func TestDiffEmptyCases(t *testing.T) { + t.Parallel() + Equal(t, "", diff(nil, nil)) Equal(t, "", diff(struct{ foo string }{}, nil)) Equal(t, "", diff(nil, struct{ foo string }{})) @@ -2572,6 +3099,9 @@ type mockTestingT struct { args []interface{} } +// Helper is like [testing.T.Helper] but does nothing. +func (mockTestingT) Helper() {} + func (m *mockTestingT) errorString() string { return fmt.Sprintf(m.errorFmt, m.args...) } @@ -2586,6 +3116,8 @@ func (m *mockTestingT) Failed() bool { } func TestFailNowWithPlainTestingT(t *testing.T) { + t.Parallel() + mockT := &mockTestingT{} Panics(t, func() { @@ -2593,14 +3125,18 @@ func TestFailNowWithPlainTestingT(t *testing.T) { }, "should panic since mockT is missing FailNow()") } -type mockFailNowTestingT struct { -} +type mockFailNowTestingT struct{} + +// Helper is like [testing.T.Helper] but does nothing. +func (mockFailNowTestingT) Helper() {} func (m *mockFailNowTestingT) Errorf(format string, args ...interface{}) {} func (m *mockFailNowTestingT) FailNow() {} func TestFailNowWithFullTestingT(t *testing.T) { + t.Parallel() + mockT := &mockFailNowTestingT{} NotPanics(t, func() { @@ -2609,7 +3145,9 @@ func TestFailNowWithFullTestingT(t *testing.T) { } func TestBytesEqual(t *testing.T) { - var cases = []struct { + t.Parallel() + + cases := []struct { a, b []byte }{ {make([]byte, 2), make([]byte, 2)}, @@ -2674,6 +3212,8 @@ func ExampleComparisonAssertionFunc() { } func TestComparisonAssertionFunc(t *testing.T) { + t.Parallel() + type iface interface { Name() string } @@ -2735,6 +3275,8 @@ func ExampleValueAssertionFunc() { } func TestValueAssertionFunc(t *testing.T) { + t.Parallel() + tests := []struct { name string value interface{} @@ -2781,6 +3323,8 @@ func ExampleBoolAssertionFunc() { } func TestBoolAssertionFunc(t *testing.T) { + t.Parallel() + tests := []struct { name string value bool @@ -2824,6 +3368,8 @@ func ExampleErrorAssertionFunc() { } func TestErrorAssertionFunc(t *testing.T) { + t.Parallel() + tests := []struct { name string err error @@ -2860,6 +3406,8 @@ func ExamplePanicAssertionFunc() { } func TestPanicAssertionFunc(t *testing.T) { + t.Parallel() + tests := []struct { name string panicFn PanicTestFunc @@ -2877,6 +3425,8 @@ func TestPanicAssertionFunc(t *testing.T) { } func TestEventuallyFalse(t *testing.T) { + t.Parallel() + mockT := new(testing.T) condition := func() bool { @@ -2887,6 +3437,8 @@ func TestEventuallyFalse(t *testing.T) { } func TestEventuallyTrue(t *testing.T) { + t.Parallel() + state := 0 condition := func() bool { defer func() { @@ -2903,13 +3455,16 @@ type errorsCapturingT struct { errors []error } +// Helper is like [testing.T.Helper] but does nothing. +func (errorsCapturingT) Helper() {} + func (t *errorsCapturingT) Errorf(format string, args ...interface{}) { t.errors = append(t.errors, fmt.Errorf(format, args...)) } -func (t *errorsCapturingT) Helper() {} - func TestEventuallyWithTFalse(t *testing.T) { + t.Parallel() + mockT := new(errorsCapturingT) condition := func(collect *CollectT) { @@ -2921,6 +3476,8 @@ func TestEventuallyWithTFalse(t *testing.T) { } func TestEventuallyWithTTrue(t *testing.T) { + t.Parallel() + mockT := new(errorsCapturingT) counter := 0 @@ -2935,6 +3492,8 @@ func TestEventuallyWithTTrue(t *testing.T) { } func TestEventuallyWithT_ConcurrencySafe(t *testing.T) { + t.Parallel() + mockT := new(errorsCapturingT) condition := func(collect *CollectT) { @@ -2947,6 +3506,8 @@ func TestEventuallyWithT_ConcurrencySafe(t *testing.T) { } func TestEventuallyWithT_ReturnsTheLatestFinishedConditionErrors(t *testing.T) { + t.Parallel() + // We'll use a channel to control whether a condition should sleep or not. mustSleep := make(chan bool, 2) mustSleep <- false @@ -2970,6 +3531,8 @@ func TestEventuallyWithT_ReturnsTheLatestFinishedConditionErrors(t *testing.T) { } func TestEventuallyWithTFailNow(t *testing.T) { + t.Parallel() + mockT := new(CollectT) condition := func(collect *CollectT) { @@ -2980,7 +3543,58 @@ func TestEventuallyWithTFailNow(t *testing.T) { Len(t, mockT.errors, 1) } +// Check that a long running condition doesn't block Eventually. +// See issue 805 (and its long tail of following issues) +func TestEventuallyTimeout(t *testing.T) { + t.Parallel() + + mockT := new(testing.T) + + NotPanics(t, func() { + done, done2 := make(chan struct{}), make(chan struct{}) + + // A condition function that returns after the Eventually timeout + condition := func() bool { + // Wait until Eventually times out and terminates + <-done + close(done2) + return true + } + + False(t, Eventually(mockT, condition, time.Millisecond, time.Microsecond)) + + close(done) + <-done2 + }) +} + +func TestEventuallySucceedQuickly(t *testing.T) { + t.Parallel() + + mockT := new(testing.T) + + condition := func() bool { return true } + + // By making the tick longer than the total duration, we expect that this test would fail if + // we didn't check the condition before the first tick elapses. + True(t, Eventually(mockT, condition, 100*time.Millisecond, time.Second)) +} + +func TestEventuallyWithTSucceedQuickly(t *testing.T) { + t.Parallel() + + mockT := new(testing.T) + + condition := func(t *CollectT) {} + + // By making the tick longer than the total duration, we expect that this test would fail if + // we didn't check the condition before the first tick elapses. + True(t, EventuallyWithT(mockT, condition, 100*time.Millisecond, time.Second)) +} + func TestNeverFalse(t *testing.T) { + t.Parallel() + condition := func() bool { return false } @@ -2990,6 +3604,8 @@ func TestNeverFalse(t *testing.T) { // TestNeverTrue checks Never with a condition that returns true on second call. func TestNeverTrue(t *testing.T) { + t.Parallel() + mockT := new(testing.T) // A list of values returned by condition. @@ -3007,30 +3623,20 @@ func TestNeverTrue(t *testing.T) { False(t, Never(mockT, condition, 100*time.Millisecond, 20*time.Millisecond)) } -// Check that a long running condition doesn't block Eventually. -// See issue 805 (and its long tail of following issues) -func TestEventuallyTimeout(t *testing.T) { - mockT := new(testing.T) - - NotPanics(t, func() { - done, done2 := make(chan struct{}), make(chan struct{}) - - // A condition function that returns after the Eventually timeout - condition := func() bool { - // Wait until Eventually times out and terminates - <-done - close(done2) - return true - } +func TestNeverFailQuickly(t *testing.T) { + t.Parallel() - False(t, Eventually(mockT, condition, time.Millisecond, time.Microsecond)) + mockT := new(testing.T) - close(done) - <-done2 - }) + // By making the tick longer than the total duration, we expect that this test would fail if + // we didn't check the condition before the first tick elapses. + condition := func() bool { return true } + False(t, Never(mockT, condition, 100*time.Millisecond, time.Second)) } func Test_validateEqualArgs(t *testing.T) { + t.Parallel() + if validateEqualArgs(func() {}, func() {}) == nil { t.Error("non-nil functions should error") } @@ -3045,13 +3651,14 @@ func Test_validateEqualArgs(t *testing.T) { } func Test_truncatingFormat(t *testing.T) { + t.Parallel() - original := strings.Repeat("a", bufio.MaxScanTokenSize-102) - result := truncatingFormat(original) + original := strings.Repeat("a", bufio.MaxScanTokenSize/2-102) + result := truncatingFormat("%#v", original) Equal(t, fmt.Sprintf("%#v", original), result, "string should not be truncated") original = original + "x" - result = truncatingFormat(original) + result = truncatingFormat("%#v", original) NotEqual(t, fmt.Sprintf("%#v", original), result, "string should have been truncated.") if !strings.HasSuffix(result, "<... truncated>") { @@ -3097,11 +3704,16 @@ func parseLabeledOutput(output string) []labeledContent { } type captureTestingT struct { - msg string + failed bool + msg string } +// Helper is like [testing.T.Helper] but does nothing. +func (captureTestingT) Helper() {} + func (ctt *captureTestingT) Errorf(format string, args ...interface{}) { ctt.msg = fmt.Sprintf(format, args...) + ctt.failed = true } func (ctt *captureTestingT) checkResultAndErrMsg(t *testing.T, expectedRes, res bool, expectedErrMsg string) { @@ -3110,15 +3722,19 @@ func (ctt *captureTestingT) checkResultAndErrMsg(t *testing.T, expectedRes, res t.Errorf("Should return %t", expectedRes) return } + if res == ctt.failed { + t.Errorf("The test result (%t) should be reflected in the testing.T type (%t)", res, !ctt.failed) + return + } contents := parseLabeledOutput(ctt.msg) if res == true { if contents != nil { - t.Errorf("Should not log an error") + t.Errorf("Should not log an error. Log output: %q", ctt.msg) } return } if contents == nil { - t.Errorf("Should log an error. Log output: %v", ctt.msg) + t.Errorf("Should log an error. Log output: %q", ctt.msg) return } for _, content := range contents { @@ -3126,13 +3742,15 @@ func (ctt *captureTestingT) checkResultAndErrMsg(t *testing.T, expectedRes, res if expectedErrMsg == content.content { return } - t.Errorf("Logged Error: %v", content.content) + t.Errorf("Recorded Error: %q", content.content) } } - t.Errorf("Should log Error: %v", expectedErrMsg) + t.Errorf("Expected Error: %q", expectedErrMsg) } func TestErrorIs(t *testing.T) { + t.Parallel() + tests := []struct { err error target error @@ -3159,13 +3777,10 @@ func TestErrorIs(t *testing.T) { "in chain: \"EOF\"\n", }, { - err: nil, - target: io.EOF, - result: false, - resultErrMsg: "" + - "Target error should be in err chain:\n" + - "expected: \"EOF\"\n" + - "in chain: \n", + err: nil, + target: io.EOF, + result: false, + resultErrMsg: "Expected error with \"EOF\" in chain but got nil.\n", }, { err: io.EOF, @@ -3203,6 +3818,8 @@ func TestErrorIs(t *testing.T) { } func TestNotErrorIs(t *testing.T) { + t.Parallel() + tests := []struct { err error target error @@ -3269,23 +3886,327 @@ func TestNotErrorIs(t *testing.T) { } func TestErrorAs(t *testing.T) { - mockT := new(testing.T) + t.Parallel() + tests := []struct { - err error - result bool + err error + result bool + resultErrMsg string }{ - {fmt.Errorf("wrap: %w", &customError{}), true}, - {io.EOF, false}, - {nil, false}, + { + err: fmt.Errorf("wrap: %w", &customError{}), + result: true, + }, + { + err: io.EOF, + result: false, + resultErrMsg: "" + + "Should be in error chain:\n" + + "expected: *assert.customError\n" + + "in chain: \"EOF\" (*errors.errorString)\n", + }, + { + err: nil, + result: false, + resultErrMsg: "" + + "An error is expected but got nil.\n" + + `expected: *assert.customError` + "\n", + }, + { + err: fmt.Errorf("abc: %w", errors.New("def")), + result: false, + resultErrMsg: "" + + "Should be in error chain:\n" + + "expected: *assert.customError\n" + + "in chain: \"abc: def\" (*fmt.wrapError)\n" + + "\t\"def\" (*errors.errorString)\n", + }, } for _, tt := range tests { tt := tt var target *customError t.Run(fmt.Sprintf("ErrorAs(%#v,%#v)", tt.err, target), func(t *testing.T) { + mockT := new(captureTestingT) res := ErrorAs(mockT, tt.err, &target) - if res != tt.result { - t.Errorf("ErrorAs(%#v,%#v) should return %t)", tt.err, target, tt.result) - } + mockT.checkResultAndErrMsg(t, tt.result, res, tt.resultErrMsg) + }) + } +} + +func TestNotErrorAs(t *testing.T) { + t.Parallel() + + tests := []struct { + err error + result bool + resultErrMsg string + }{ + { + err: fmt.Errorf("wrap: %w", &customError{}), + result: false, + resultErrMsg: "" + + "Target error should not be in err chain:\n" + + "found: *assert.customError\n" + + "in chain: \"wrap: fail\" (*fmt.wrapError)\n" + + "\t\"fail\" (*assert.customError)\n", + }, + { + err: io.EOF, + result: true, + }, + { + err: nil, + result: true, + }, + } + for _, tt := range tests { + tt := tt + var target *customError + t.Run(fmt.Sprintf("NotErrorAs(%#v,%#v)", tt.err, target), func(t *testing.T) { + mockT := new(captureTestingT) + res := NotErrorAs(mockT, tt.err, &target) + mockT.checkResultAndErrMsg(t, tt.result, res, tt.resultErrMsg) }) } } + +func TestLenWithSliceTooLongToPrint(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) + longSlice := make([]int, 1_000_000) + Len(mockT, longSlice, 1) + Contains(t, mockT.errorString(), ` + Error Trace: + Error: "[0 0 0`) + Contains(t, mockT.errorString(), `<... truncated>" should have 1 item(s), but has 1000000`) +} + +func TestContainsWithSliceTooLongToPrint(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) + longSlice := make([]int, 1_000_000) + Contains(mockT, longSlice, 1) + Contains(t, mockT.errorString(), ` + Error Trace: + Error: []int{0, 0, 0,`) + Contains(t, mockT.errorString(), `<... truncated> does not contain 1`) +} + +func TestNotContainsWithSliceTooLongToPrint(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) + longSlice := make([]int, 1_000_000) + NotContains(mockT, longSlice, 0) + Contains(t, mockT.errorString(), ` + Error Trace: + Error: []int{0, 0, 0,`) + Contains(t, mockT.errorString(), `<... truncated> should not contain 0`) +} + +func TestSubsetWithSliceTooLongToPrint(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) + longSlice := make([]int, 1_000_000) + Subset(mockT, longSlice, []int{1}) + Contains(t, mockT.errorString(), ` + Error Trace: + Error: []int{0, 0, 0,`) + Contains(t, mockT.errorString(), `<... truncated> does not contain 1`) +} + +func TestSubsetWithMapTooLongToPrint(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) + longSlice := make([]int, 1_000_000) + Subset(mockT, map[bool][]int{true: longSlice}, map[bool][]int{false: longSlice}) + Contains(t, mockT.errorString(), ` + Error Trace: + Error: map[bool][]int{true:[]int{0, 0, 0,`) + Contains(t, mockT.errorString(), `<... truncated> does not contain map[bool][]int{false:[]int{0, 0, 0,`) +} + +func TestNotSubsetWithSliceTooLongToPrint(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) + longSlice := make([]int, 1_000_000) + NotSubset(mockT, longSlice, longSlice) + Contains(t, mockT.errorString(), ` + Error Trace: + Error: ['\x00' '\x00' '\x00'`) + Contains(t, mockT.errorString(), `<... truncated> is a subset of ['\x00' '\x00' '\x00'`) +} + +func TestNotSubsetWithMapTooLongToPrint(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) + longSlice := make([]int, 1_000_000) + NotSubset(mockT, map[int][]int{1: longSlice}, map[int][]int{1: longSlice}) + Contains(t, mockT.errorString(), ` + Error Trace: + Error: map['\x01':['\x00' '\x00' '\x00'`) + Contains(t, mockT.errorString(), `<... truncated> is a subset of map['\x01':['\x00' '\x00' '\x00'`) +} + +func TestSameWithSliceTooLongToPrint(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) + longSlice := make([]int, 1_000_000) + Same(mockT, &[]int{}, &longSlice) + Contains(t, mockT.errorString(), `&[]int{0, 0, 0,`) +} + +func TestNotSameWithSliceTooLongToPrint(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) + longSlice := make([]int, 1_000_000) + NotSame(mockT, &longSlice, &longSlice) + Contains(t, mockT.errorString(), `&[]int{0, 0, 0,`) +} + +func TestNilWithSliceTooLongToPrint(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) + longSlice := make([]int, 1_000_000) + Nil(mockT, &longSlice) + Contains(t, mockT.errorString(), ` + Error Trace: + Error: Expected nil, but got: &[]int{0, 0, 0,`) + Contains(t, mockT.errorString(), `<... truncated>`) +} + +func TestEmptyWithSliceTooLongToPrint(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) + longSlice := make([]int, 1_000_000) + Empty(mockT, longSlice) + Contains(t, mockT.errorString(), ` + Error Trace: + Error: Should be empty, but was [0 0 0`) + Contains(t, mockT.errorString(), `<... truncated>`) +} + +func TestNotEqualWithSliceTooLongToPrint(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) + longSlice := make([]int, 1_000_000) + NotEqual(mockT, longSlice, longSlice) + Contains(t, mockT.errorString(), ` + Error Trace: + Error: Should not be: []int{0, 0, 0,`) + Contains(t, mockT.errorString(), `<... truncated>`) +} + +func TestNotEqualValuesWithSliceTooLongToPrint(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) + longSlice := make([]int, 1_000_000) + NotEqualValues(mockT, longSlice, longSlice) + Contains(t, mockT.errorString(), ` + Error Trace: + Error: Should not be: []int{0, 0, 0,`) + Contains(t, mockT.errorString(), `<... truncated>`) +} + +func TestNoErrorWithErrorTooLongToPrint(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) + longSlice := make([]int, 1_000_000) + NoError(mockT, fmt.Errorf("long: %v", longSlice)) + Contains(t, mockT.errorString(), ` + Error Trace: + Error: Received unexpected error: + long: [0 0 0`) + Contains(t, mockT.errorString(), `<... truncated>`) +} + +func TestEqualErrorWithErrorTooLongToPrint(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) + longSlice := make([]int, 1_000_000) + EqualError(mockT, fmt.Errorf("long: %v", longSlice), "EOF") + Contains(t, mockT.errorString(), ` + Error Trace: + Error: Error message not equal: + expected: "EOF" + actual : "long: [0 0 0`) + Contains(t, mockT.errorString(), `<... truncated>`) +} + +func TestErrorContainsWithErrorTooLongToPrint(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) + longSlice := make([]int, 1_000_000) + ErrorContains(mockT, fmt.Errorf("long: %v", longSlice), "EOF") + Contains(t, mockT.errorString(), ` + Error Trace: + Error: Error "long: [0 0 0`) + Contains(t, mockT.errorString(), `<... truncated> does not contain "EOF"`) +} + +func TestZeroWithSliceTooLongToPrint(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) + longSlice := make([]int, 1_000_000) + Zero(mockT, longSlice) + Contains(t, mockT.errorString(), ` + Error Trace: + Error: Should be zero, but was [0 0 0`) + Contains(t, mockT.errorString(), `<... truncated>`) +} + +func TestErrorIsWithErrorTooLongToPrint(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) + longSlice := make([]int, 1_000_000) + ErrorIs(mockT, fmt.Errorf("long: %v", longSlice), fmt.Errorf("also: %v", longSlice)) + Contains(t, mockT.errorString(), ` + Error Trace: + Error: Target error should be in err chain: + expected: "also: [0 0 0`) + Contains(t, mockT.errorString(), `<... truncated> + in chain: "long: [0 0 0`) +} + +func TestNotErrorIsWithErrorTooLongToPrint(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) + longSlice := make([]int, 1_000_000) + err := fmt.Errorf("long: %v", longSlice) + NotErrorIs(mockT, err, err) + Contains(t, mockT.errorString(), ` + Error Trace: + Error: Target error should not be in err chain: + found: "long: [0 0 0`) + Contains(t, mockT.errorString(), `<... truncated> + in chain: "long: [0 0 0`) +} + +func TestErrorAsWithErrorTooLongToPrint(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) + longSlice := make([]int, 1_000_000) + var target *customError + ErrorAs(mockT, fmt.Errorf("long: %v", longSlice), &target) + Contains(t, mockT.errorString(), ` + Error Trace: + Error: Should be in error chain: + expected: *assert.customError`) + Contains(t, mockT.errorString(), ` + in chain: "long: [0 0 0`) + Contains(t, mockT.errorString(), "<... truncated>") +} + +func TestNotErrorAsWithErrorTooLongToPrint(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) + longSlice := make([]int, 1_000_000) + var target *customError + NotErrorAs(mockT, fmt.Errorf("long: %v %w", longSlice, &customError{}), &target) + Contains(t, mockT.errorString(), ` + Error Trace: + Error: Target error should not be in err chain: + found: *assert.customError`) + Contains(t, mockT.errorString(), ` + in chain: "long: [0 0 0`) + Contains(t, mockT.errorString(), "<... truncated>") +} diff --git a/assert/doc.go b/assert/doc.go index 4953981d3..c111589c7 100644 --- a/assert/doc.go +++ b/assert/doc.go @@ -1,5 +1,9 @@ // Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. // +// # Note +// +// All functions in this package return a bool value indicating whether the assertion has passed. +// // # Example Usage // // The following is a complete example using assert in a standard test function: @@ -36,8 +40,8 @@ // // # Assertions // -// Assertions allow you to easily write test code, and are global funcs in the `assert` package. -// All assertion functions take, as the first argument, the `*testing.T` object provided by the +// Assertions allow you to easily write test code, and are global funcs in the assert package. +// All assertion functions take, as the first argument, the [*testing.T] object provided by the // testing framework. This allows the assertion funcs to write the failings and other details to // the correct place. // diff --git a/assert/forward_assertions_test.go b/assert/forward_assertions_test.go index 496d3c0db..5523422fe 100644 --- a/assert/forward_assertions_test.go +++ b/assert/forward_assertions_test.go @@ -8,6 +8,8 @@ import ( ) func TestImplementsWrapper(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if !assert.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject)) { @@ -19,6 +21,8 @@ func TestImplementsWrapper(t *testing.T) { } func TestIsTypeWrapper(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if !assert.IsType(new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) { @@ -31,6 +35,8 @@ func TestIsTypeWrapper(t *testing.T) { } func TestEqualWrapper(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if !assert.Equal("Hello World", "Hello World") { @@ -51,6 +57,8 @@ func TestEqualWrapper(t *testing.T) { } func TestEqualValuesWrapper(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if !assert.EqualValues(uint32(10), int32(10)) { @@ -59,6 +67,8 @@ func TestEqualValuesWrapper(t *testing.T) { } func TestNotNilWrapper(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if !assert.NotNil(new(AssertionTesterConformingObject)) { @@ -71,6 +81,8 @@ func TestNotNilWrapper(t *testing.T) { } func TestNilWrapper(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if !assert.Nil(nil) { @@ -83,6 +95,8 @@ func TestNilWrapper(t *testing.T) { } func TestTrueWrapper(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if !assert.True(true) { @@ -95,6 +109,8 @@ func TestTrueWrapper(t *testing.T) { } func TestFalseWrapper(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if !assert.False(false) { @@ -107,6 +123,8 @@ func TestFalseWrapper(t *testing.T) { } func TestExactlyWrapper(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) a := float32(1) @@ -134,6 +152,7 @@ func TestExactlyWrapper(t *testing.T) { } func TestNotEqualWrapper(t *testing.T) { + t.Parallel() assert := New(new(testing.T)) @@ -155,6 +174,7 @@ func TestNotEqualWrapper(t *testing.T) { } func TestNotEqualValuesWrapper(t *testing.T) { + t.Parallel() assert := New(new(testing.T)) @@ -179,6 +199,7 @@ func TestNotEqualValuesWrapper(t *testing.T) { } func TestContainsWrapper(t *testing.T) { + t.Parallel() assert := New(new(testing.T)) list := []string{"Foo", "Bar"} @@ -200,6 +221,7 @@ func TestContainsWrapper(t *testing.T) { } func TestNotContainsWrapper(t *testing.T) { + t.Parallel() assert := New(new(testing.T)) list := []string{"Foo", "Bar"} @@ -221,6 +243,7 @@ func TestNotContainsWrapper(t *testing.T) { } func TestConditionWrapper(t *testing.T) { + t.Parallel() assert := New(new(testing.T)) @@ -235,6 +258,7 @@ func TestConditionWrapper(t *testing.T) { } func TestDidPanicWrapper(t *testing.T) { + t.Parallel() if funcDidPanic, _, _ := didPanic(func() { panic("Panic!") @@ -250,6 +274,7 @@ func TestDidPanicWrapper(t *testing.T) { } func TestPanicsWrapper(t *testing.T) { + t.Parallel() assert := New(new(testing.T)) @@ -267,6 +292,7 @@ func TestPanicsWrapper(t *testing.T) { } func TestNotPanicsWrapper(t *testing.T) { + t.Parallel() assert := New(new(testing.T)) @@ -284,6 +310,8 @@ func TestNotPanicsWrapper(t *testing.T) { } func TestNoErrorWrapper(t *testing.T) { + t.Parallel() + assert := New(t) mockAssert := New(new(testing.T)) @@ -300,6 +328,8 @@ func TestNoErrorWrapper(t *testing.T) { } func TestErrorWrapper(t *testing.T) { + t.Parallel() + assert := New(t) mockAssert := New(new(testing.T)) @@ -316,6 +346,8 @@ func TestErrorWrapper(t *testing.T) { } func TestErrorContainsWrapper(t *testing.T) { + t.Parallel() + assert := New(t) mockAssert := New(new(testing.T)) @@ -335,6 +367,8 @@ func TestErrorContainsWrapper(t *testing.T) { } func TestEqualErrorWrapper(t *testing.T) { + t.Parallel() + assert := New(t) mockAssert := New(new(testing.T)) @@ -352,6 +386,8 @@ func TestEqualErrorWrapper(t *testing.T) { } func TestEmptyWrapper(t *testing.T) { + t.Parallel() + assert := New(t) mockAssert := New(new(testing.T)) @@ -370,6 +406,8 @@ func TestEmptyWrapper(t *testing.T) { } func TestNotEmptyWrapper(t *testing.T) { + t.Parallel() + assert := New(t) mockAssert := New(new(testing.T)) @@ -388,6 +426,8 @@ func TestNotEmptyWrapper(t *testing.T) { } func TestLenWrapper(t *testing.T) { + t.Parallel() + assert := New(t) mockAssert := New(new(testing.T)) @@ -428,6 +468,8 @@ func TestLenWrapper(t *testing.T) { } func TestWithinDurationWrapper(t *testing.T) { + t.Parallel() + assert := New(t) mockAssert := New(new(testing.T)) a := time.Now() @@ -447,6 +489,8 @@ func TestWithinDurationWrapper(t *testing.T) { } func TestInDeltaWrapper(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) True(t, assert.InDelta(1.001, 1, 0.01), "|1.001 - 1| <= 0.01") @@ -481,6 +525,8 @@ func TestInDeltaWrapper(t *testing.T) { } func TestInEpsilonWrapper(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) cases := []struct { @@ -519,6 +565,7 @@ func TestInEpsilonWrapper(t *testing.T) { } func TestRegexpWrapper(t *testing.T) { + t.Parallel() assert := New(new(testing.T)) @@ -546,7 +593,7 @@ func TestRegexpWrapper(t *testing.T) { } for _, tc := range cases { - False(t, assert.Regexp(tc.rx, tc.str), "Expected \"%s\" to not match \"%s\"", tc.rx, tc.str) + False(t, assert.Regexp(tc.rx, tc.str), "Expected %q to not match %q", tc.rx, tc.str) False(t, assert.Regexp(regexp.MustCompile(tc.rx), tc.str)) True(t, assert.NotRegexp(tc.rx, tc.str)) True(t, assert.NotRegexp(regexp.MustCompile(tc.rx), tc.str)) @@ -554,6 +601,8 @@ func TestRegexpWrapper(t *testing.T) { } func TestZeroWrapper(t *testing.T) { + t.Parallel() + assert := New(t) mockAssert := New(new(testing.T)) @@ -567,6 +616,8 @@ func TestZeroWrapper(t *testing.T) { } func TestNotZeroWrapper(t *testing.T) { + t.Parallel() + assert := New(t) mockAssert := New(new(testing.T)) @@ -580,6 +631,8 @@ func TestNotZeroWrapper(t *testing.T) { } func TestJSONEqWrapper_EqualSONString(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if !assert.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) { t.Error("JSONEq should return true") @@ -588,6 +641,8 @@ func TestJSONEqWrapper_EqualSONString(t *testing.T) { } func TestJSONEqWrapper_EquivalentButNotEqual(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if !assert.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) { t.Error("JSONEq should return true") @@ -596,6 +651,8 @@ func TestJSONEqWrapper_EquivalentButNotEqual(t *testing.T) { } func TestJSONEqWrapper_HashOfArraysAndHashes(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if !assert.JSONEq("{\r\n\t\"numeric\": 1.5,\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]],\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\"\r\n}", "{\r\n\t\"numeric\": 1.5,\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\",\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]]\r\n}") { @@ -604,6 +661,8 @@ func TestJSONEqWrapper_HashOfArraysAndHashes(t *testing.T) { } func TestJSONEqWrapper_Array(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if !assert.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) { t.Error("JSONEq should return true") @@ -612,6 +671,8 @@ func TestJSONEqWrapper_Array(t *testing.T) { } func TestJSONEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if assert.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) { t.Error("JSONEq should return false") @@ -619,6 +680,8 @@ func TestJSONEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { } func TestJSONEqWrapper_HashesNotEquivalent(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if assert.JSONEq(`{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) { t.Error("JSONEq should return false") @@ -626,6 +689,8 @@ func TestJSONEqWrapper_HashesNotEquivalent(t *testing.T) { } func TestJSONEqWrapper_ActualIsNotJSON(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if assert.JSONEq(`{"foo": "bar"}`, "Not JSON") { t.Error("JSONEq should return false") @@ -633,6 +698,8 @@ func TestJSONEqWrapper_ActualIsNotJSON(t *testing.T) { } func TestJSONEqWrapper_ExpectedIsNotJSON(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if assert.JSONEq("Not JSON", `{"foo": "bar", "hello": "world"}`) { t.Error("JSONEq should return false") @@ -640,6 +707,8 @@ func TestJSONEqWrapper_ExpectedIsNotJSON(t *testing.T) { } func TestJSONEqWrapper_ExpectedAndActualNotJSON(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if assert.JSONEq("Not JSON", "Not JSON") { t.Error("JSONEq should return false") @@ -647,6 +716,8 @@ func TestJSONEqWrapper_ExpectedAndActualNotJSON(t *testing.T) { } func TestJSONEqWrapper_ArraysOfDifferentOrder(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if assert.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) { t.Error("JSONEq should return false") @@ -654,6 +725,8 @@ func TestJSONEqWrapper_ArraysOfDifferentOrder(t *testing.T) { } func TestYAMLEqWrapper_EqualYAMLString(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if !assert.YAMLEq(`{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) { t.Error("YAMLEq should return true") @@ -662,6 +735,8 @@ func TestYAMLEqWrapper_EqualYAMLString(t *testing.T) { } func TestYAMLEqWrapper_EquivalentButNotEqual(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if !assert.YAMLEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) { t.Error("YAMLEq should return true") @@ -670,6 +745,8 @@ func TestYAMLEqWrapper_EquivalentButNotEqual(t *testing.T) { } func TestYAMLEqWrapper_HashOfArraysAndHashes(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) expected := ` numeric: 1.5 @@ -702,6 +779,8 @@ array: } func TestYAMLEqWrapper_Array(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if !assert.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) { t.Error("YAMLEq should return true") @@ -710,6 +789,8 @@ func TestYAMLEqWrapper_Array(t *testing.T) { } func TestYAMLEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if assert.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) { t.Error("YAMLEq should return false") @@ -717,6 +798,8 @@ func TestYAMLEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { } func TestYAMLEqWrapper_HashesNotEquivalent(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if assert.YAMLEq(`{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) { t.Error("YAMLEq should return false") @@ -724,6 +807,8 @@ func TestYAMLEqWrapper_HashesNotEquivalent(t *testing.T) { } func TestYAMLEqWrapper_ActualIsSimpleString(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if assert.YAMLEq(`{"foo": "bar"}`, "Simple String") { t.Error("YAMLEq should return false") @@ -731,6 +816,8 @@ func TestYAMLEqWrapper_ActualIsSimpleString(t *testing.T) { } func TestYAMLEqWrapper_ExpectedIsSimpleString(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if assert.YAMLEq("Simple String", `{"foo": "bar", "hello": "world"}`) { t.Error("YAMLEq should return false") @@ -738,6 +825,8 @@ func TestYAMLEqWrapper_ExpectedIsSimpleString(t *testing.T) { } func TestYAMLEqWrapper_ExpectedAndActualSimpleString(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if !assert.YAMLEq("Simple String", "Simple String") { t.Error("YAMLEq should return true") @@ -745,6 +834,8 @@ func TestYAMLEqWrapper_ExpectedAndActualSimpleString(t *testing.T) { } func TestYAMLEqWrapper_ArraysOfDifferentOrder(t *testing.T) { + t.Parallel() + assert := New(new(testing.T)) if assert.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) { t.Error("YAMLEq should return false") diff --git a/assert/http_assertions.go b/assert/http_assertions.go index 861ed4b7c..5a6bb75f2 100644 --- a/assert/http_assertions.go +++ b/assert/http_assertions.go @@ -138,7 +138,7 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, contains := strings.Contains(body, fmt.Sprint(str)) if !contains { - Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body), msgAndArgs...) + Fail(t, fmt.Sprintf("Expected response body for %q to contain %q but found %q", url+"?"+values.Encode(), str, body), msgAndArgs...) } return contains @@ -158,7 +158,7 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url strin contains := strings.Contains(body, fmt.Sprint(str)) if contains { - Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body), msgAndArgs...) + Fail(t, fmt.Sprintf("Expected response body for %q to NOT contain %q but found %q", url+"?"+values.Encode(), str, body), msgAndArgs...) } return !contains diff --git a/assert/http_assertions_test.go b/assert/http_assertions_test.go index dd84f02f1..a1dd4540d 100644 --- a/assert/http_assertions_test.go +++ b/assert/http_assertions_test.go @@ -31,6 +31,8 @@ func httpStatusCode(w http.ResponseWriter, r *http.Request) { } func TestHTTPSuccess(t *testing.T) { + t.Parallel() + assert := New(t) mockT1 := new(testing.T) @@ -59,6 +61,8 @@ func TestHTTPSuccess(t *testing.T) { } func TestHTTPRedirect(t *testing.T) { + t.Parallel() + assert := New(t) mockT1 := new(mockTestingT) @@ -83,6 +87,8 @@ func TestHTTPRedirect(t *testing.T) { } func TestHTTPError(t *testing.T) { + t.Parallel() + assert := New(t) mockT1 := new(testing.T) @@ -107,6 +113,8 @@ func TestHTTPError(t *testing.T) { } func TestHTTPStatusCode(t *testing.T) { + t.Parallel() + assert := New(t) mockT1 := new(testing.T) @@ -131,6 +139,8 @@ func TestHTTPStatusCode(t *testing.T) { } func TestHTTPStatusesWrapper(t *testing.T) { + t.Parallel() + assert := New(t) mockAssert := New(new(testing.T)) @@ -153,6 +163,8 @@ func httpHelloName(w http.ResponseWriter, r *http.Request) { } func TestHTTPRequestWithNoParams(t *testing.T) { + t.Parallel() + var got *http.Request handler := func(w http.ResponseWriter, r *http.Request) { got = r @@ -166,6 +178,8 @@ func TestHTTPRequestWithNoParams(t *testing.T) { } func TestHTTPRequestWithParams(t *testing.T) { + t.Parallel() + var got *http.Request handler := func(w http.ResponseWriter, r *http.Request) { got = r @@ -182,6 +196,8 @@ func TestHTTPRequestWithParams(t *testing.T) { } func TestHttpBody(t *testing.T) { + t.Parallel() + assert := New(t) mockT := new(mockTestingT) @@ -201,6 +217,8 @@ func TestHttpBody(t *testing.T) { } func TestHttpBodyWrappers(t *testing.T) { + t.Parallel() + assert := New(t) mockAssert := New(new(testing.T)) diff --git a/assert/yaml/yaml_custom.go b/assert/yaml/yaml_custom.go index bc32dc96c..5a74c4f4d 100644 --- a/assert/yaml/yaml_custom.go +++ b/assert/yaml/yaml_custom.go @@ -1,5 +1,4 @@ //go:build testify_yaml_custom && !testify_yaml_fail && !testify_yaml_default -// +build testify_yaml_custom,!testify_yaml_fail,!testify_yaml_default // Package yaml is an implementation of YAML functions that calls a pluggable implementation. // @@ -15,7 +14,7 @@ // import assertYaml "github.com/stretchr/testify/assert/yaml" // // func init() { -// assertYaml.Unmarshall = func (in []byte, out interface{}) error { +// assertYaml.Unmarshal = func (in []byte, out interface{}) error { // // ... // return nil // } diff --git a/assert/yaml/yaml_default.go b/assert/yaml/yaml_default.go index 9a92eed67..0bae80e34 100644 --- a/assert/yaml/yaml_default.go +++ b/assert/yaml/yaml_default.go @@ -1,11 +1,10 @@ //go:build !testify_yaml_fail && !testify_yaml_custom -// +build !testify_yaml_fail,!testify_yaml_custom // Package yaml is just an indirection to handle YAML deserialization. // // This package is just an indirection that allows the builder to override the // indirection with an alternative implementation of this package that uses -// another implemantation of YAML deserialization. This allows to not either not +// another implementation of YAML deserialization. This allows to not either not // use YAML deserialization at all, or to use another implementation than // [gopkg.in/yaml.v3] (for example for license compatibility reasons, see [PR #1120]). // diff --git a/assert/yaml/yaml_fail.go b/assert/yaml/yaml_fail.go index e78f7dfe6..8041803fd 100644 --- a/assert/yaml/yaml_fail.go +++ b/assert/yaml/yaml_fail.go @@ -1,5 +1,4 @@ //go:build testify_yaml_fail && !testify_yaml_custom && !testify_yaml_default -// +build testify_yaml_fail,!testify_yaml_custom,!testify_yaml_default // Package yaml is an implementation of YAML functions that always fail. // diff --git a/doc.go b/doc.go index 96407f3be..2076714cc 100644 --- a/doc.go +++ b/doc.go @@ -1,16 +1,17 @@ -// ** We are working on testify v2 and would love to hear what you'd like to see in it, have your say here: https://cutt.ly/testify ** -// Package testify is a set of packages that provide many tools for testifying that your code will behave as you intend. +// Module testify is a set of packages that provide many tools for testifying that your code will behave as you intend. // -// testify contains the following packages: +// Testify contains the following packages: // -// The assert package provides a comprehensive set of assertion functions that tie in to the Go testing system. +// The [github.com/stretchr/testify/assert] package provides a comprehensive set of assertion functions that tie in to [the Go testing system]. +// The [github.com/stretchr/testify/require] package provides the same assertions but as fatal checks. // -// The mock package provides a system by which it is possible to mock your objects and verify calls are happening as expected. +// The [github.com/stretchr/testify/mock] package provides a system by which it is possible to mock your objects and verify calls are happening as expected. // -// The suite package provides a basic structure for using structs as testing suites, and methods on those structs as tests. It includes setup/teardown functionality in the way of interfaces. +// The [github.com/stretchr/testify/suite] package provides a basic structure for using structs as testing suites, and methods on those structs as tests. It includes setup/teardown functionality in the way of interfaces. // // A [golangci-lint] compatible linter for testify is available called [testifylint]. // +// [the Go testing system]: https://go.dev/doc/code#Testing // [golangci-lint]: https://golangci-lint.run/ // [testifylint]: https://github.com/Antonboom/testifylint package testify diff --git a/go.mod b/go.mod index a72480837..5c9729831 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,10 @@ go 1.17 require ( github.com/davecgh/go-spew v1.1.1 - github.com/pmezard/go-difflib v1.0.0 - github.com/stretchr/objx v0.5.2 + github.com/stretchr/objx v0.5.2 // To avoid a cycle the version of testify used by objx should be excluded below gopkg.in/yaml.v3 v3.0.1 ) + +// Break dependency cycle with objx. +// See https://github.com/stretchr/objx/pull/140 +exclude github.com/stretchr/testify v1.8.4 diff --git a/go.sum b/go.sum index d42acda31..c1fbe5c0a 100644 --- a/go.sum +++ b/go.sum @@ -1,18 +1,9 @@ -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 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.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 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.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/vendor/github.com/pmezard/go-difflib/LICENSE b/internal/difflib/LICENSE similarity index 95% rename from vendor/github.com/pmezard/go-difflib/LICENSE rename to internal/difflib/LICENSE index c67dad612..485be13c6 100644 --- a/vendor/github.com/pmezard/go-difflib/LICENSE +++ b/internal/difflib/LICENSE @@ -24,4 +24,4 @@ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/vendor/github.com/pmezard/go-difflib/difflib/difflib.go b/internal/difflib/difflib.go similarity index 77% rename from vendor/github.com/pmezard/go-difflib/difflib/difflib.go rename to internal/difflib/difflib.go index 003e99fad..9984599b4 100644 --- a/vendor/github.com/pmezard/go-difflib/difflib/difflib.go +++ b/internal/difflib/difflib.go @@ -8,11 +8,14 @@ // // - unified_diff // -// - context_diff -// // Getting unified diffs was the main goal of the port. Keep in mind this code // is mostly suitable to output text differences in a human friendly way, there // are no guarantees generated diffs are consumable by patch(1). +// +// This package was adopted from [github.com/pmezard/go-difflib] which +// is no longer maintained. +// +// [github.com/pmezard/go-difflib]: https://github.com/pmezard/go-difflib package difflib import ( @@ -37,13 +40,6 @@ func max(a, b int) int { return b } -func calculateRatio(matches, length int) float64 { - if length > 0 { - return 2.0 * float64(matches) / float64(length) - } - return 1.0 -} - type Match struct { A int B int @@ -103,14 +99,6 @@ func NewMatcher(a, b []string) *SequenceMatcher { return &m } -func NewMatcherWithJunk(a, b []string, autoJunk bool, - isJunk func(string) bool) *SequenceMatcher { - - m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk} - m.SetSeqs(a, b) - return &m -} - // Set two sequences to be compared. func (m *SequenceMatcher) SetSeqs(a, b []string) { m.SetSeq1(a) @@ -199,12 +187,15 @@ func (m *SequenceMatcher) isBJunk(s string) bool { // If IsJunk is not defined: // // Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where -// alo <= i <= i+k <= ahi -// blo <= j <= j+k <= bhi +// +// alo <= i <= i+k <= ahi +// blo <= j <= j+k <= bhi +// // and for all (i',j',k') meeting those conditions, -// k >= k' -// i <= i' -// and if i == i', j <= j' +// +// k >= k' +// i <= i' +// and if i == i', j <= j' // // In other words, of all maximal matching blocks, return one that // starts earliest in a, and of all those maximal matching blocks that @@ -451,66 +442,6 @@ func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode { return groups } -// Return a measure of the sequences' similarity (float in [0,1]). -// -// Where T is the total number of elements in both sequences, and -// M is the number of matches, this is 2.0*M / T. -// Note that this is 1 if the sequences are identical, and 0 if -// they have nothing in common. -// -// .Ratio() is expensive to compute if you haven't already computed -// .GetMatchingBlocks() or .GetOpCodes(), in which case you may -// want to try .QuickRatio() or .RealQuickRation() first to get an -// upper bound. -func (m *SequenceMatcher) Ratio() float64 { - matches := 0 - for _, m := range m.GetMatchingBlocks() { - matches += m.Size - } - return calculateRatio(matches, len(m.a)+len(m.b)) -} - -// Return an upper bound on ratio() relatively quickly. -// -// This isn't defined beyond that it is an upper bound on .Ratio(), and -// is faster to compute. -func (m *SequenceMatcher) QuickRatio() float64 { - // viewing a and b as multisets, set matches to the cardinality - // of their intersection; this counts the number of matches - // without regard to order, so is clearly an upper bound - if m.fullBCount == nil { - m.fullBCount = map[string]int{} - for _, s := range m.b { - m.fullBCount[s] = m.fullBCount[s] + 1 - } - } - - // avail[x] is the number of times x appears in 'b' less the - // number of times we've seen it in 'a' so far ... kinda - avail := map[string]int{} - matches := 0 - for _, s := range m.a { - n, ok := avail[s] - if !ok { - n = m.fullBCount[s] - } - avail[s] = n - 1 - if n > 0 { - matches += 1 - } - } - return calculateRatio(matches, len(m.a)+len(m.b)) -} - -// Return an upper bound on ratio() very quickly. -// -// This isn't defined beyond that it is an upper bound on .Ratio(), and -// is faster to compute than either .Ratio() or .QuickRatio(). -func (m *SequenceMatcher) RealQuickRatio() float64 { - la, lb := len(m.a), len(m.b) - return calculateRatio(min(la, lb), la+lb) -} - // Convert range to the "ed" format func formatRangeUnified(start, stop int) string { // Per the diff spec at http://www.unix.org/single_unix_specification/ @@ -652,117 +583,6 @@ func formatRangeContext(start, stop int) string { return fmt.Sprintf("%d,%d", beginning, beginning+length-1) } -type ContextDiff UnifiedDiff - -// Compare two sequences of lines; generate the delta as a context diff. -// -// Context diffs are a compact way of showing line changes and a few -// lines of context. The number of context lines is set by diff.Context -// which defaults to three. -// -// By default, the diff control lines (those with *** or ---) are -// created with a trailing newline. -// -// For inputs that do not have trailing newlines, set the diff.Eol -// argument to "" so that the output will be uniformly newline free. -// -// The context diff format normally has a header for filenames and -// modification times. Any or all of these may be specified using -// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate. -// The modification times are normally expressed in the ISO 8601 format. -// If not specified, the strings default to blanks. -func WriteContextDiff(writer io.Writer, diff ContextDiff) error { - buf := bufio.NewWriter(writer) - defer buf.Flush() - var diffErr error - wf := func(format string, args ...interface{}) { - _, err := buf.WriteString(fmt.Sprintf(format, args...)) - if diffErr == nil && err != nil { - diffErr = err - } - } - ws := func(s string) { - _, err := buf.WriteString(s) - if diffErr == nil && err != nil { - diffErr = err - } - } - - if len(diff.Eol) == 0 { - diff.Eol = "\n" - } - - prefix := map[byte]string{ - 'i': "+ ", - 'd': "- ", - 'r': "! ", - 'e': " ", - } - - started := false - m := NewMatcher(diff.A, diff.B) - for _, g := range m.GetGroupedOpCodes(diff.Context) { - if !started { - started = true - fromDate := "" - if len(diff.FromDate) > 0 { - fromDate = "\t" + diff.FromDate - } - toDate := "" - if len(diff.ToDate) > 0 { - toDate = "\t" + diff.ToDate - } - if diff.FromFile != "" || diff.ToFile != "" { - wf("*** %s%s%s", diff.FromFile, fromDate, diff.Eol) - wf("--- %s%s%s", diff.ToFile, toDate, diff.Eol) - } - } - - first, last := g[0], g[len(g)-1] - ws("***************" + diff.Eol) - - range1 := formatRangeContext(first.I1, last.I2) - wf("*** %s ****%s", range1, diff.Eol) - for _, c := range g { - if c.Tag == 'r' || c.Tag == 'd' { - for _, cc := range g { - if cc.Tag == 'i' { - continue - } - for _, line := range diff.A[cc.I1:cc.I2] { - ws(prefix[cc.Tag] + line) - } - } - break - } - } - - range2 := formatRangeContext(first.J1, last.J2) - wf("--- %s ----%s", range2, diff.Eol) - for _, c := range g { - if c.Tag == 'r' || c.Tag == 'i' { - for _, cc := range g { - if cc.Tag == 'd' { - continue - } - for _, line := range diff.B[cc.J1:cc.J2] { - ws(prefix[cc.Tag] + line) - } - } - break - } - } - } - return diffErr -} - -// Like WriteContextDiff but returns the diff a string. -func GetContextDiffString(diff ContextDiff) (string, error) { - w := &bytes.Buffer{} - err := WriteContextDiff(w, diff) - return string(w.Bytes()), err -} - // Split a string on "\n" while preserving them. The output can be used // as input for UnifiedDiff and ContextDiff structures. func SplitLines(s string) []string { diff --git a/internal/difflib/difflib_test.go b/internal/difflib/difflib_test.go new file mode 100644 index 000000000..21d37cb42 --- /dev/null +++ b/internal/difflib/difflib_test.go @@ -0,0 +1,258 @@ +package difflib + +import ( + "bytes" + "fmt" + "math" + "reflect" + "strings" + "testing" +) + +func assertAlmostEqual(t *testing.T, a, b float64, places int) { + if math.Abs(a-b) > math.Pow10(-places) { + t.Errorf("%.7f != %.7f", a, b) + } +} + +func assertEqual(t *testing.T, a, b interface{}) { + if !reflect.DeepEqual(a, b) { + t.Errorf("%v != %v", a, b) + } +} + +func splitChars(s string) []string { + chars := make([]string, 0, len(s)) + // Assume ASCII inputs + for i := 0; i != len(s); i++ { + chars = append(chars, string(s[i])) + } + return chars +} + +func TestGetOptCodes(t *testing.T) { + a := "qabxcd" + b := "abycdf" + s := NewMatcher(splitChars(a), splitChars(b)) + w := &bytes.Buffer{} + for _, op := range s.GetOpCodes() { + fmt.Fprintf(w, "%s a[%d:%d], (%s) b[%d:%d] (%s)\n", string(op.Tag), + op.I1, op.I2, a[op.I1:op.I2], op.J1, op.J2, b[op.J1:op.J2]) + } + result := string(w.Bytes()) + expected := `d a[0:1], (q) b[0:0] () +e a[1:3], (ab) b[0:2] (ab) +r a[3:4], (x) b[2:3] (y) +e a[4:6], (cd) b[3:5] (cd) +i a[6:6], () b[5:6] (f) +` + if expected != result { + t.Errorf("unexpected op codes: \n%s", result) + } +} + +func TestGroupedOpCodes(t *testing.T) { + a := []string{} + for i := 0; i != 39; i++ { + a = append(a, fmt.Sprintf("%02d", i)) + } + b := []string{} + b = append(b, a[:8]...) + b = append(b, " i") + b = append(b, a[8:19]...) + b = append(b, " x") + b = append(b, a[20:22]...) + b = append(b, a[27:34]...) + b = append(b, " y") + b = append(b, a[35:]...) + s := NewMatcher(a, b) + w := &bytes.Buffer{} + for _, g := range s.GetGroupedOpCodes(-1) { + fmt.Fprintf(w, "group\n") + for _, op := range g { + fmt.Fprintf(w, " %s, %d, %d, %d, %d\n", string(op.Tag), + op.I1, op.I2, op.J1, op.J2) + } + } + result := string(w.Bytes()) + expected := `group + e, 5, 8, 5, 8 + i, 8, 8, 8, 9 + e, 8, 11, 9, 12 +group + e, 16, 19, 17, 20 + r, 19, 20, 20, 21 + e, 20, 22, 21, 23 + d, 22, 27, 23, 23 + e, 27, 30, 23, 26 +group + e, 31, 34, 27, 30 + r, 34, 35, 30, 31 + e, 35, 38, 31, 34 +` + if expected != result { + t.Errorf("unexpected op codes: \n%s", result) + } +} + +func rep(s string, count int) string { + return strings.Repeat(s, count) +} + +func TestWithAsciiOneInsert(t *testing.T) { + sm := NewMatcher(splitChars(rep("b", 100)), + splitChars("a"+rep("b", 100))) + assertEqual(t, sm.GetOpCodes(), + []OpCode{{'i', 0, 0, 0, 1}, {'e', 0, 100, 1, 101}}) + assertEqual(t, len(sm.bPopular), 0) + + sm = NewMatcher(splitChars(rep("b", 100)), + splitChars(rep("b", 50)+"a"+rep("b", 50))) + assertEqual(t, sm.GetOpCodes(), + []OpCode{{'e', 0, 50, 0, 50}, {'i', 50, 50, 50, 51}, {'e', 50, 100, 51, 101}}) + assertEqual(t, len(sm.bPopular), 0) +} + +func TestWithAsciiOnDelete(t *testing.T) { + sm := NewMatcher(splitChars(rep("a", 40)+"c"+rep("b", 40)), + splitChars(rep("a", 40)+rep("b", 40))) + assertEqual(t, sm.GetOpCodes(), + []OpCode{{'e', 0, 40, 0, 40}, {'d', 40, 41, 40, 40}, {'e', 41, 81, 40, 80}}) +} + +func TestSFBugsComparingEmptyLists(t *testing.T) { + groups := NewMatcher(nil, nil).GetGroupedOpCodes(-1) + assertEqual(t, len(groups), 0) + diff := UnifiedDiff{ + FromFile: "Original", + ToFile: "Current", + Context: 3, + } + result, err := GetUnifiedDiffString(diff) + assertEqual(t, err, nil) + assertEqual(t, result, "") +} + +func TestOutputFormatRangeFormatUnified(t *testing.T) { + // Per the diff spec at http://www.unix.org/single_unix_specification/ + // + // Each field shall be of the form: + // %1d", if the range contains exactly one line, + // and: + // "%1d,%1d", , otherwise. + // If a range is empty, its beginning line number shall be the number of + // the line just before the range, or 0 if the empty range starts the file. + fm := formatRangeUnified + assertEqual(t, fm(3, 3), "3,0") + assertEqual(t, fm(3, 4), "4") + assertEqual(t, fm(3, 5), "4,2") + assertEqual(t, fm(3, 6), "4,3") + assertEqual(t, fm(0, 0), "0,0") +} + +func TestOutputFormatRangeFormatContext(t *testing.T) { + // Per the diff spec at http://www.unix.org/single_unix_specification/ + // + // The range of lines in file1 shall be written in the following format + // if the range contains two or more lines: + // "*** %d,%d ****\n", , + // and the following format otherwise: + // "*** %d ****\n", + // The ending line number of an empty range shall be the number of the preceding line, + // or 0 if the range is at the start of the file. + // + // Next, the range of lines in file2 shall be written in the following format + // if the range contains two or more lines: + // "--- %d,%d ----\n", , + // and the following format otherwise: + // "--- %d ----\n", + fm := formatRangeContext + assertEqual(t, fm(3, 3), "3") + assertEqual(t, fm(3, 4), "4") + assertEqual(t, fm(3, 5), "4,5") + assertEqual(t, fm(3, 6), "4,6") + assertEqual(t, fm(0, 0), "0") +} + +func TestOutputFormatTabDelimiter(t *testing.T) { + diff := UnifiedDiff{ + A: splitChars("one"), + B: splitChars("two"), + FromFile: "Original", + FromDate: "2005-01-26 23:30:50", + ToFile: "Current", + ToDate: "2010-04-12 10:20:52", + Eol: "\n", + } + ud, err := GetUnifiedDiffString(diff) + assertEqual(t, err, nil) + assertEqual(t, SplitLines(ud)[:2], []string{ + "--- Original\t2005-01-26 23:30:50\n", + "+++ Current\t2010-04-12 10:20:52\n", + }) +} + +func TestOutputFormatNoTrailingTabOnEmptyFiledate(t *testing.T) { + diff := UnifiedDiff{ + A: splitChars("one"), + B: splitChars("two"), + FromFile: "Original", + ToFile: "Current", + Eol: "\n", + } + ud, err := GetUnifiedDiffString(diff) + assertEqual(t, err, nil) + assertEqual(t, SplitLines(ud)[:2], []string{"--- Original\n", "+++ Current\n"}) +} + +func TestOmitFilenames(t *testing.T) { + diff := UnifiedDiff{ + A: SplitLines("o\nn\ne\n"), + B: SplitLines("t\nw\no\n"), + Eol: "\n", + } + ud, err := GetUnifiedDiffString(diff) + assertEqual(t, err, nil) + assertEqual(t, SplitLines(ud), []string{ + "@@ -0,0 +1,2 @@\n", + "+t\n", + "+w\n", + "@@ -2,2 +3,0 @@\n", + "-n\n", + "-e\n", + "\n", + }) +} + +func TestSplitLines(t *testing.T) { + allTests := []struct { + input string + want []string + }{ + {"foo", []string{"foo\n"}}, + {"foo\nbar", []string{"foo\n", "bar\n"}}, + {"foo\nbar\n", []string{"foo\n", "bar\n", "\n"}}, + } + for _, test := range allTests { + assertEqual(t, SplitLines(test.input), test.want) + } +} + +func benchmarkSplitLines(b *testing.B, count int) { + str := strings.Repeat("foo\n", count) + + b.ResetTimer() + + n := 0 + for i := 0; i < b.N; i++ { + n += len(SplitLines(str)) + } +} + +func BenchmarkSplitLines100(b *testing.B) { + benchmarkSplitLines(b, 100) +} + +func BenchmarkSplitLines10000(b *testing.B) { + benchmarkSplitLines(b, 10000) +} diff --git a/mock/mock.go b/mock/mock.go index d5eb1ef55..c95eeeca8 100644 --- a/mock/mock.go +++ b/mock/mock.go @@ -12,10 +12,9 @@ import ( "time" "github.com/davecgh/go-spew/spew" - "github.com/pmezard/go-difflib/difflib" "github.com/stretchr/objx" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/internal/difflib" ) // regex for GCCGO functions @@ -80,12 +79,12 @@ type Call struct { requires []*Call } -func newCall(parent *Mock, methodName string, callerInfo []string, methodArguments ...interface{}) *Call { +func newCall(parent *Mock, methodName string, callerInfo []string, methodArguments Arguments, returnArguments Arguments) *Call { return &Call{ Parent: parent, Method: methodName, Arguments: methodArguments, - ReturnArguments: make([]interface{}, 0), + ReturnArguments: returnArguments, callerInfo: callerInfo, Repeatability: 0, WaitFor: nil, @@ -208,9 +207,16 @@ func (c *Call) On(methodName string, arguments ...interface{}) *Call { return c.Parent.On(methodName, arguments...) } -// Unset removes a mock handler from being called. +// Unset removes all mock handlers that satisfy the call instance arguments from being +// called. Only supported on call instances with static input arguments. +// +// For example, the only handler remaining after the following would be "MyMethod(2, 2)": // -// test.On("func", mock.Anything).Unset() +// Mock. +// On("MyMethod", 2, 2).Return(0). +// On("MyMethod", 3, 3).Return(0). +// On("MyMethod", Anything, Anything).Return(0) +// Mock.On("MyMethod", 3, 3).Unset() func (c *Call) Unset() *Call { var unlockOnce sync.Once @@ -256,7 +262,7 @@ func (c *Call) Unset() *Call { // calls have been called as expected. The referenced calls may be from the // same mock instance and/or other mock instances. // -// Mock.On("Do").Return(nil).Notbefore( +// Mock.On("Do").Return(nil).NotBefore( // Mock.On("Init").Return(nil) // ) func (c *Call) NotBefore(calls ...*Call) *Call { @@ -273,6 +279,20 @@ func (c *Call) NotBefore(calls ...*Call) *Call { return c } +// InOrder defines the order in which the calls should be made +// +// For example: +// +// InOrder( +// Mock.On("init").Return(nil), +// Mock.On("Do").Return(nil), +// ) +func InOrder(calls ...*Call) { + for i := 1; i < len(calls); i++ { + calls[i].NotBefore(calls[i-1]) + } +} + // Mock is the workhorse used to track activity on another object. // For an example of its usage, refer to the "Example Usage" section at the top // of this document. @@ -317,7 +337,10 @@ func (m *Mock) TestData() objx.Map { Setting expectations */ -// Test sets the test struct variable of the mock object +// Test sets the [TestingT] on which errors will be reported, otherwise errors +// will cause a panic. +// Test should not be called on an object that is going to be used in a +// goroutine other than the one running the test function. func (m *Mock) Test(t TestingT) { m.mutex.Lock() defer m.mutex.Unlock() @@ -351,7 +374,8 @@ func (m *Mock) On(methodName string, arguments ...interface{}) *Call { m.mutex.Lock() defer m.mutex.Unlock() - c := newCall(m, methodName, assert.CallerInfo(), arguments...) + + c := newCall(m, methodName, assert.CallerInfo(), arguments, make([]interface{}, 0)) m.ExpectedCalls = append(m.ExpectedCalls, c) return c } @@ -479,7 +503,7 @@ func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Argumen // expected call found, but it has already been called with repeatable times if call != nil { m.mutex.Unlock() - m.fail("\nassert: mock: The method has been called over %d times.\n\tEither do one more Mock.On(\"%s\").Return(...), or remove extra call.\n\tThis call was unexpected:\n\t\t%s\n\tat: %s", call.totalCalls, methodName, callString(methodName, arguments, true), assert.CallerInfo()) + m.fail("\nassert: mock: The method has been called over %d times.\n\tEither do one more Mock.On(%#v).Return(...), or remove extra call.\n\tThis call was unexpected:\n\t\t%s\n\tat: %s", call.totalCalls, methodName, callString(methodName, arguments, true), assert.CallerInfo()) } // we have to fail here - because we don't know what to do // as the return arguments. This is because: @@ -491,14 +515,15 @@ func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Argumen m.mutex.Unlock() if closestCall != nil { - m.fail("\n\nmock: Unexpected Method Call\n-----------------------------\n\n%s\n\nThe closest call I have is: \n\n%s\n\n%s\nDiff: %s", + m.fail("\n\nmock: Unexpected Method Call\n-----------------------------\n\n%s\n\nThe closest call I have is: \n\n%s\n\n%s\nDiff: %s\nat: %s\n", callString(methodName, arguments, true), callString(methodName, closestCall.Arguments, true), diffArguments(closestCall.Arguments, arguments), strings.TrimSpace(mismatch), + assert.CallerInfo(), ) } else { - m.fail("\nassert: mock: I don't know what to return because the method call was unexpected.\n\tEither do Mock.On(\"%s\").Return(...) first, or remove the %s() call.\n\tThis method was unexpected:\n\t\t%s\n\tat: %s", methodName, methodName, callString(methodName, arguments, true), assert.CallerInfo()) + m.fail("\nassert: mock: I don't know what to return because the method call was unexpected.\n\tEither do Mock.On(%#v).Return(...) first, or remove the %s() call.\n\tThis method was unexpected:\n\t\t%s\n\tat: %s", methodName, methodName, callString(methodName, arguments, true), assert.CallerInfo()) } } @@ -529,7 +554,7 @@ func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Argumen call.totalCalls++ // add the call - m.Calls = append(m.Calls, *newCall(m, methodName, assert.CallerInfo(), arguments...)) + m.Calls = append(m.Calls, *newCall(m, methodName, assert.CallerInfo(), arguments, call.ReturnArguments)) m.mutex.Unlock() // block if specified @@ -578,11 +603,12 @@ func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool { h.Helper() } for _, obj := range testObjects { - if m, ok := obj.(*Mock); ok { - t.Logf("Deprecated mock.AssertExpectationsForObjects(myMock.Mock) use mock.AssertExpectationsForObjects(myMock)") - obj = m + m, ok := obj.(assertExpectationiser) + if !ok { + t.Errorf("Invalid test object type %T. Expected reference to a mock.Mock, eg: 'AssertExpectationsForObjects(t, myMock)' or 'AssertExpectationsForObjects(t, &myMock.Mock)'", obj) + continue + } - m := obj.(assertExpectationiser) if !m.AssertExpectations(t) { t.Logf("Expectations didn't match for Mock: %+v", reflect.TypeOf(m)) return false @@ -645,7 +671,7 @@ func (m *Mock) AssertNumberOfCalls(t TestingT, methodName string, expectedCalls actualCalls++ } } - return assert.Equal(t, expectedCalls, actualCalls, fmt.Sprintf("Expected number of calls (%d) does not match the actual number of calls (%d).", expectedCalls, actualCalls)) + return assert.Equal(t, expectedCalls, actualCalls, fmt.Sprintf("Expected number of calls (%d) of method %s does not match the actual number of calls (%d).", expectedCalls, methodName, actualCalls)) } // AssertCalled asserts that the method was called. @@ -686,8 +712,8 @@ func (m *Mock) AssertNotCalled(t TestingT, methodName string, arguments ...inter return true } -// IsMethodCallable checking that the method can be called -// If the method was called more than `Repeatability` return false +// IsMethodCallable returns true if given methodName and arguments have an +// unsatisfied expected call registered in the Mock. func (m *Mock) IsMethodCallable(t TestingT, methodName string, arguments ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -807,25 +833,28 @@ type IsTypeArgument struct { // For example: // // args.Assert(t, IsType(""), IsType(0)) +// +// Mock cannot match interface types because the contained type will be passed +// to both IsType and Mock.Called, for the zero value of all interfaces this +// will be type. func IsType(t interface{}) *IsTypeArgument { return &IsTypeArgument{t: reflect.TypeOf(t)} } -// FunctionalOptionsArgument is a struct that contains the type and value of an functional option argument -// for use when type checking. +// FunctionalOptionsArgument contains a list of functional options arguments +// expected for use when matching a list of arguments. type FunctionalOptionsArgument struct { - value interface{} + values []interface{} } // String returns the string representation of FunctionalOptionsArgument func (f *FunctionalOptionsArgument) String() string { var name string - tValue := reflect.ValueOf(f.value) - if tValue.Len() > 0 { - name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String() + if len(f.values) > 0 { + name = "[]" + reflect.TypeOf(f.values[0]).String() } - return strings.Replace(fmt.Sprintf("%#v", f.value), "[]interface {}", name, 1) + return strings.Replace(fmt.Sprintf("%#v", f.values), "[]interface {}", name, 1) } // FunctionalOptions returns an [FunctionalOptionsArgument] object containing @@ -833,10 +862,10 @@ func (f *FunctionalOptionsArgument) String() string { // // For example: // -// Assert(t, FunctionalOptions(foo.Opt1("strValue"), foo.Opt2(613))) -func FunctionalOptions(value ...interface{}) *FunctionalOptionsArgument { +// args.Assert(t, FunctionalOptions(foo.Opt1("strValue"), foo.Opt2(613))) +func FunctionalOptions(values ...interface{}) *FunctionalOptionsArgument { return &FunctionalOptionsArgument{ - value: value, + values: values, } } @@ -987,23 +1016,20 @@ func (args Arguments) Diff(objects []interface{}) (string, int) { actualT := reflect.TypeOf(actual) if actualT != expected.t { differences++ - output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected.t.Name(), actualT.Name(), actualFmt) + output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, safeTypeName(expected.t), safeTypeName(actualT), actualFmt) } case *FunctionalOptionsArgument: - t := expected.value - var name string - tValue := reflect.ValueOf(t) - if tValue.Len() > 0 { - name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String() + if len(expected.values) > 0 { + name = "[]" + reflect.TypeOf(expected.values[0]).String() } - tName := reflect.TypeOf(t).Name() - if name != reflect.TypeOf(actual).String() && tValue.Len() != 0 { + const tName = "[]interface{}" + if name != reflect.TypeOf(actual).String() && len(expected.values) != 0 { differences++ output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, tName, reflect.TypeOf(actual).Name(), actualFmt) } else { - if ef, af := assertOpts(t, actual); ef == "" && af == "" { + if ef, af := assertOpts(expected.values, actual); ef == "" && af == "" { // match output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, tName, tName) } else { @@ -1119,6 +1145,15 @@ func (args Arguments) Bool(index int) bool { return s } +// safeTypeName returns the reflect.Type's name without causing a panic. +// If the provided reflect.Type is nil, it returns the placeholder string "" +func safeTypeName(t reflect.Type) string { + if t == nil { + return "" + } + return t.Name() +} + func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) { t := reflect.TypeOf(v) k := t.Kind() @@ -1192,32 +1227,38 @@ type tHelper interface { func assertOpts(expected, actual interface{}) (expectedFmt, actualFmt string) { expectedOpts := reflect.ValueOf(expected) actualOpts := reflect.ValueOf(actual) + + var expectedFuncs []*runtime.Func var expectedNames []string for i := 0; i < expectedOpts.Len(); i++ { - expectedNames = append(expectedNames, funcName(expectedOpts.Index(i).Interface())) + f := runtimeFunc(expectedOpts.Index(i).Interface()) + expectedFuncs = append(expectedFuncs, f) + expectedNames = append(expectedNames, funcName(f)) } + var actualFuncs []*runtime.Func var actualNames []string for i := 0; i < actualOpts.Len(); i++ { - actualNames = append(actualNames, funcName(actualOpts.Index(i).Interface())) + f := runtimeFunc(actualOpts.Index(i).Interface()) + actualFuncs = append(actualFuncs, f) + actualNames = append(actualNames, funcName(f)) } - if !assert.ObjectsAreEqual(expectedNames, actualNames) { + + if expectedOpts.Len() != actualOpts.Len() { expectedFmt = fmt.Sprintf("%v", expectedNames) actualFmt = fmt.Sprintf("%v", actualNames) return } for i := 0; i < expectedOpts.Len(); i++ { - expectedOpt := expectedOpts.Index(i).Interface() - actualOpt := actualOpts.Index(i).Interface() - - expectedFunc := expectedNames[i] - actualFunc := actualNames[i] - if expectedFunc != actualFunc { - expectedFmt = expectedFunc - actualFmt = actualFunc + if !isFuncSame(expectedFuncs[i], actualFuncs[i]) { + expectedFmt = expectedNames[i] + actualFmt = actualNames[i] return } + expectedOpt := expectedOpts.Index(i).Interface() + actualOpt := actualOpts.Index(i).Interface() + ot := reflect.TypeOf(expectedOpt) var expectedValues []reflect.Value var actualValues []reflect.Value @@ -1235,9 +1276,9 @@ func assertOpts(expected, actual interface{}) (expectedFmt, actualFmt string) { reflect.ValueOf(actualOpt).Call(actualValues) for i := 0; i < ot.NumIn(); i++ { - if !assert.ObjectsAreEqual(expectedValues[i].Interface(), actualValues[i].Interface()) { - expectedFmt = fmt.Sprintf("%s %+v", expectedNames[i], expectedValues[i].Interface()) - actualFmt = fmt.Sprintf("%s %+v", expectedNames[i], actualValues[i].Interface()) + if expectedArg, actualArg := expectedValues[i].Interface(), actualValues[i].Interface(); !assert.ObjectsAreEqual(expectedArg, actualArg) { + expectedFmt = fmt.Sprintf("%s(%T) -> %#v", expectedNames[i], expectedArg, expectedArg) + actualFmt = fmt.Sprintf("%s(%T) -> %#v", expectedNames[i], actualArg, actualArg) return } } @@ -1246,7 +1287,25 @@ func assertOpts(expected, actual interface{}) (expectedFmt, actualFmt string) { return "", "" } -func funcName(opt interface{}) string { - n := runtime.FuncForPC(reflect.ValueOf(opt).Pointer()).Name() - return strings.TrimSuffix(path.Base(n), path.Ext(n)) +func runtimeFunc(opt interface{}) *runtime.Func { + return runtime.FuncForPC(reflect.ValueOf(opt).Pointer()) +} + +func funcName(f *runtime.Func) string { + name := f.Name() + trimmed := strings.TrimSuffix(path.Base(name), path.Ext(name)) + splitted := strings.Split(trimmed, ".") + + if len(splitted) == 0 { + return trimmed + } + + return splitted[len(splitted)-1] +} + +func isFuncSame(f1, f2 *runtime.Func) bool { + f1File, f1Loc := f1.FileLine(f1.Entry()) + f2File, f2Loc := f2.FileLine(f2.Entry()) + + return f1File == f2File && f1Loc == f2Loc } diff --git a/mock/mock_test.go b/mock/mock_test.go index b80a8a75b..3dc9e0b1e 100644 --- a/mock/mock_test.go +++ b/mock/mock_test.go @@ -1,10 +1,12 @@ package mock import ( + "context" "errors" "fmt" "regexp" "runtime" + "strconv" "sync" "testing" "time" @@ -50,11 +52,22 @@ func OpStr(s string) OptionFn { o.str = s } } + +func OpBytes(b []byte) OptionFn { + return func(m *options) { + m.str = string(b) + } +} + func (i *TestExampleImplementation) TheExampleMethodFunctionalOptions(x string, opts ...OptionFn) error { args := i.Called(x, opts) return args.Error(0) } +func TheExampleMethodFunctionalOptionsIndirect(i *TestExampleImplementation) { + i.TheExampleMethodFunctionalOptions("test", OpNum(1), OpStr("foo")) +} + //go:noinline func (i *TestExampleImplementation) TheExampleMethod2(yesorno bool) { i.Called(yesorno) @@ -121,6 +134,9 @@ type MockTestingT struct { logfCount, errorfCount, failNowCount int } +// Helper is like [testing.T.Helper] but does nothing. +func (MockTestingT) Helper() {} + const mockTestingTFailNowCalled = "FailNow was called" func (m *MockTestingT) Logf(string, ...interface{}) { @@ -149,6 +165,7 @@ func (m *MockTestingT) FailNow() { */ func Test_Mock_TestData(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -160,6 +177,7 @@ func Test_Mock_TestData(t *testing.T) { } func Test_Mock_On(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -170,6 +188,8 @@ func Test_Mock_On(t *testing.T) { } func Test_Mock_Chained_On(t *testing.T) { + t.Parallel() + // make a test impl object var mockedService = new(TestExampleImplementation) @@ -201,6 +221,7 @@ func Test_Mock_Chained_On(t *testing.T) { } func Test_Mock_On_WithArgs(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -213,6 +234,7 @@ func Test_Mock_On_WithArgs(t *testing.T) { } func Test_Mock_On_WithFuncArg(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -234,6 +256,8 @@ func Test_Mock_On_WithFuncArg(t *testing.T) { } func Test_Mock_On_WithIntArgMatcher(t *testing.T) { + t.Parallel() + var mockedService TestExampleImplementation mockedService.On("TheExampleMethod", @@ -257,6 +281,8 @@ func Test_Mock_On_WithIntArgMatcher(t *testing.T) { } func Test_Mock_On_WithArgMatcherThatPanics(t *testing.T) { + t.Parallel() + var mockedService TestExampleImplementation mockedService.On("TheExampleMethod2", MatchedBy(func(_ interface{}) bool { @@ -283,6 +309,8 @@ func Test_Mock_On_WithArgMatcherThatPanics(t *testing.T) { } func TestMock_WithTest(t *testing.T) { + t.Parallel() + var ( mockedService TestExampleImplementation mockedTest MockTestingT @@ -311,6 +339,8 @@ func TestMock_WithTest(t *testing.T) { } func Test_Mock_On_WithPtrArgMatcher(t *testing.T) { + t.Parallel() + var mockedService TestExampleImplementation mockedService.On("TheExampleMethod3", @@ -331,6 +361,8 @@ func Test_Mock_On_WithPtrArgMatcher(t *testing.T) { } func Test_Mock_On_WithFuncArgMatcher(t *testing.T) { + t.Parallel() + var mockedService TestExampleImplementation fixture1, fixture2 := errors.New("fixture1"), errors.New("fixture2") @@ -355,6 +387,8 @@ func Test_Mock_On_WithFuncArgMatcher(t *testing.T) { } func Test_Mock_On_WithInterfaceArgMatcher(t *testing.T) { + t.Parallel() + var mockedService TestExampleImplementation mockedService.On("TheExampleMethod4", @@ -365,6 +399,8 @@ func Test_Mock_On_WithInterfaceArgMatcher(t *testing.T) { } func Test_Mock_On_WithChannelArgMatcher(t *testing.T) { + t.Parallel() + var mockedService TestExampleImplementation mockedService.On("TheExampleMethod5", @@ -375,6 +411,8 @@ func Test_Mock_On_WithChannelArgMatcher(t *testing.T) { } func Test_Mock_On_WithMapArgMatcher(t *testing.T) { + t.Parallel() + var mockedService TestExampleImplementation mockedService.On("TheExampleMethod6", @@ -385,6 +423,8 @@ func Test_Mock_On_WithMapArgMatcher(t *testing.T) { } func Test_Mock_On_WithSliceArgMatcher(t *testing.T) { + t.Parallel() + var mockedService TestExampleImplementation mockedService.On("TheExampleMethod7", @@ -395,6 +435,7 @@ func Test_Mock_On_WithSliceArgMatcher(t *testing.T) { } func Test_Mock_On_WithVariadicFunc(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -417,6 +458,7 @@ func Test_Mock_On_WithVariadicFunc(t *testing.T) { } func Test_Mock_On_WithMixedVariadicFunc(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -440,6 +482,7 @@ func Test_Mock_On_WithMixedVariadicFunc(t *testing.T) { } func Test_Mock_On_WithVariadicFuncWithInterface(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -461,6 +504,7 @@ func Test_Mock_On_WithVariadicFuncWithInterface(t *testing.T) { } func Test_Mock_On_WithVariadicFuncWithEmptyInterfaceArray(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -484,6 +528,8 @@ func Test_Mock_On_WithVariadicFuncWithEmptyInterfaceArray(t *testing.T) { } func Test_Mock_On_WithFuncPanics(t *testing.T) { + t.Parallel() + // make a test impl object var mockedService = new(TestExampleImplementation) @@ -493,6 +539,7 @@ func Test_Mock_On_WithFuncPanics(t *testing.T) { } func Test_Mock_On_WithFuncTypeArg(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -512,6 +559,8 @@ func Test_Mock_On_WithFuncTypeArg(t *testing.T) { } func Test_Mock_Unset(t *testing.T) { + t.Parallel() + // make a test impl object var mockedService = new(TestExampleImplementation) @@ -540,6 +589,8 @@ func Test_Mock_Unset(t *testing.T) { // Since every time you call On it creates a new object // the last time you call Unset it will only unset the last call func Test_Mock_Chained_UnsetOnlyUnsetsLastCall(t *testing.T) { + t.Parallel() + // make a test impl object var mockedService = new(TestExampleImplementation) @@ -574,6 +625,8 @@ func Test_Mock_Chained_UnsetOnlyUnsetsLastCall(t *testing.T) { } func Test_Mock_UnsetIfAlreadyUnsetFails(t *testing.T) { + t.Parallel() + // make a test impl object var mockedService = new(TestExampleImplementation) @@ -593,6 +646,8 @@ func Test_Mock_UnsetIfAlreadyUnsetFails(t *testing.T) { } func Test_Mock_UnsetByOnMethodSpec(t *testing.T) { + t.Parallel() + // make a test impl object var mockedService = new(TestExampleImplementation) @@ -614,6 +669,8 @@ func Test_Mock_UnsetByOnMethodSpec(t *testing.T) { } func Test_Mock_UnsetByOnMethodSpecAmongOthers(t *testing.T) { + t.Parallel() + // make a test impl object var mockedService = new(TestExampleImplementation) @@ -656,6 +713,8 @@ func Test_Mock_UnsetByOnMethodSpecAmongOthers(t *testing.T) { } func Test_Mock_Unset_WithFuncPanics(t *testing.T) { + t.Parallel() + // make a test impl object var mockedService = new(TestExampleImplementation) mock1 := mockedService.On("TheExampleMethod", 1) @@ -667,6 +726,7 @@ func Test_Mock_Unset_WithFuncPanics(t *testing.T) { } func Test_Mock_Return(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -691,6 +751,7 @@ func Test_Mock_Return(t *testing.T) { } func Test_Mock_Panic(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -714,6 +775,7 @@ func Test_Mock_Panic(t *testing.T) { } func Test_Mock_Return_WaitUntil(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -741,6 +803,7 @@ func Test_Mock_Return_WaitUntil(t *testing.T) { } func Test_Mock_Return_After(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -767,6 +830,7 @@ func Test_Mock_Return_After(t *testing.T) { } func Test_Mock_Return_Run(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -799,6 +863,8 @@ func Test_Mock_Return_Run(t *testing.T) { } func Test_Mock_Return_Run_Out_Of_Order(t *testing.T) { + t.Parallel() + // make a test impl object var mockedService = new(TestExampleImplementation) f := func(args Arguments) { @@ -824,6 +890,7 @@ func Test_Mock_Return_Run_Out_Of_Order(t *testing.T) { } func Test_Mock_Return_Once(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -848,6 +915,7 @@ func Test_Mock_Return_Once(t *testing.T) { } func Test_Mock_Return_Twice(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -873,6 +941,7 @@ func Test_Mock_Return_Twice(t *testing.T) { } func Test_Mock_Return_Times(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -898,6 +967,7 @@ func Test_Mock_Return_Times(t *testing.T) { } func Test_Mock_Return_Nothing(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -918,6 +988,8 @@ func Test_Mock_Return_Nothing(t *testing.T) { } func Test_Mock_Return_NotBefore_In_Order(t *testing.T) { + t.Parallel() + var mockedService = new(TestExampleImplementation) b := mockedService. @@ -937,7 +1009,31 @@ func Test_Mock_Return_NotBefore_In_Order(t *testing.T) { }) } +func Test_Mock_Return_InOrder_Uses_NotBefore(t *testing.T) { + t.Parallel() + + var mockedService = new(TestExampleImplementation) + + InOrder( + mockedService. + On("TheExampleMethod", 1, 2, 3). + Return(4, nil), + mockedService. + On("TheExampleMethod2", true). + Return(), + ) + + require.NotPanics(t, func() { + mockedService.TheExampleMethod(1, 2, 3) + }) + require.NotPanics(t, func() { + mockedService.TheExampleMethod2(true) + }) +} + func Test_Mock_Return_NotBefore_Out_Of_Order(t *testing.T) { + t.Parallel() + var mockedService = new(TestExampleImplementation) b := mockedService. @@ -967,7 +1063,40 @@ TheExampleMethod(int,int,int) }) } +func Test_Mock_Return_InOrder_Uses_NotBefore_Out_Of_Order(t *testing.T) { + t.Parallel() + + var mockedService = new(TestExampleImplementation) + + InOrder( + mockedService. + On("TheExampleMethod", 1, 2, 3). + Return(4, nil).Twice(), + mockedService. + On("TheExampleMethod2", true). + Return(), + ) + + expectedPanicString := `mock: Unexpected Method Call +----------------------------- + +TheExampleMethod2(bool) + 0: true + +Must not be called before: + +TheExampleMethod(int,int,int) + 0: 1 + 1: 2 + 2: 3` + require.PanicsWithValue(t, expectedPanicString, func() { + mockedService.TheExampleMethod2(true) + }) +} + func Test_Mock_Return_NotBefore_Not_Enough_Times(t *testing.T) { + t.Parallel() + var mockedService = new(TestExampleImplementation) b := mockedService. @@ -1001,6 +1130,8 @@ TheExampleMethod(int,int,int) } func Test_Mock_Return_NotBefore_Different_Mock_In_Order(t *testing.T) { + t.Parallel() + var ( mockedService1 = new(TestExampleImplementation) mockedService2 = new(TestExampleImplementation) @@ -1022,7 +1153,10 @@ func Test_Mock_Return_NotBefore_Different_Mock_In_Order(t *testing.T) { mockedService2.TheExampleMethod2(true) }) } + func Test_Mock_Return_NotBefore_Different_Mock_Out_Of_Order(t *testing.T) { + t.Parallel() + var ( mockedService1 = new(TestExampleImplementation) mockedService2 = new(TestExampleImplementation) @@ -1056,6 +1190,8 @@ TheExampleMethod(int,int,int) } func Test_Mock_Return_NotBefore_In_Order_With_Non_Dependant(t *testing.T) { + t.Parallel() + var mockedService = new(TestExampleImplementation) a := mockedService. @@ -1096,6 +1232,8 @@ func Test_Mock_Return_NotBefore_In_Order_With_Non_Dependant(t *testing.T) { } func Test_Mock_Return_NotBefore_Orphan_Call(t *testing.T) { + t.Parallel() + var mockedService = new(TestExampleImplementation) require.PanicsWithValue(t, "not before calls must be created with Mock.On()", func() { @@ -1107,6 +1245,7 @@ func Test_Mock_Return_NotBefore_Orphan_Call(t *testing.T) { } func Test_Mock_findExpectedCall(t *testing.T) { + t.Parallel() m := new(Mock) m.On("One", 1).Return("one") @@ -1126,6 +1265,7 @@ func Test_Mock_findExpectedCall(t *testing.T) { } func Test_Mock_findExpectedCall_For_Unknown_Method(t *testing.T) { + t.Parallel() m := new(Mock) m.On("One", 1).Return("one") @@ -1139,6 +1279,7 @@ func Test_Mock_findExpectedCall_For_Unknown_Method(t *testing.T) { } func Test_Mock_findExpectedCall_Respects_Repeatability(t *testing.T) { + t.Parallel() m := new(Mock) m.On("One", 1).Return("one") @@ -1169,6 +1310,7 @@ func Test_Mock_findExpectedCall_Respects_Repeatability(t *testing.T) { } func Test_callString(t *testing.T) { + t.Parallel() assert.Equal(t, `Method(int,bool,string)`, callString("Method", []interface{}{1, true, "something"}, false)) assert.Equal(t, `Method()`, callString("Method", []interface{}{nil}, false)) @@ -1176,6 +1318,7 @@ func Test_callString(t *testing.T) { } func Test_Mock_Called(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1203,10 +1346,11 @@ func asyncCall(m *Mock, ch chan Arguments) { } func Test_Mock_Called_blocks(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) - mockedService.Mock.On("asyncCall", 1, 2, 3).Return(5, "6", true).After(2 * time.Millisecond) + mockedService.Mock.On("asyncCall", 1, 2, 3).Return(5, "6", true).After(20 * time.Millisecond) ch := make(chan Arguments) @@ -1215,7 +1359,7 @@ func Test_Mock_Called_blocks(t *testing.T) { select { case <-ch: t.Fatal("should have waited") - case <-time.After(1 * time.Millisecond): + case <-time.After(10 * time.Millisecond): } returnArguments := <-ch @@ -1236,6 +1380,7 @@ func Test_Mock_Called_blocks(t *testing.T) { } func Test_Mock_Called_For_Bounded_Repeatability(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1277,6 +1422,7 @@ func Test_Mock_Called_For_Bounded_Repeatability(t *testing.T) { } func Test_Mock_Called_For_SetTime_Expectation(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1293,6 +1439,7 @@ func Test_Mock_Called_For_SetTime_Expectation(t *testing.T) { } func Test_Mock_Called_Unexpected(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1304,6 +1451,7 @@ func Test_Mock_Called_Unexpected(t *testing.T) { } func Test_AssertExpectationsForObjects_Helper(t *testing.T) { + t.Parallel() var mockedService1 = new(TestExampleImplementation) var mockedService2 = new(TestExampleImplementation) @@ -1324,6 +1472,7 @@ func Test_AssertExpectationsForObjects_Helper(t *testing.T) { } func Test_AssertExpectationsForObjects_Helper_Failed(t *testing.T) { + t.Parallel() var mockedService1 = new(TestExampleImplementation) var mockedService2 = new(TestExampleImplementation) @@ -1343,6 +1492,7 @@ func Test_AssertExpectationsForObjects_Helper_Failed(t *testing.T) { } func Test_Mock_AssertExpectations(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1360,6 +1510,7 @@ func Test_Mock_AssertExpectations(t *testing.T) { } func Test_Mock_AssertExpectations_Placeholder_NoArgs(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1378,6 +1529,7 @@ func Test_Mock_AssertExpectations_Placeholder_NoArgs(t *testing.T) { } func Test_Mock_AssertExpectations_Placeholder(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1401,6 +1553,7 @@ func Test_Mock_AssertExpectations_Placeholder(t *testing.T) { } func Test_Mock_AssertExpectations_With_Pointers(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1422,6 +1575,7 @@ func Test_Mock_AssertExpectations_With_Pointers(t *testing.T) { } func Test_Mock_AssertExpectationsCustomType(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1439,6 +1593,7 @@ func Test_Mock_AssertExpectationsCustomType(t *testing.T) { } func Test_Mock_AssertExpectationsFunctionalOptionsType(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1456,6 +1611,7 @@ func Test_Mock_AssertExpectationsFunctionalOptionsType(t *testing.T) { } func Test_Mock_AssertExpectationsFunctionalOptionsType_Empty(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1472,7 +1628,56 @@ func Test_Mock_AssertExpectationsFunctionalOptionsType_Empty(t *testing.T) { } +func Test_Mock_AssertExpectationsFunctionalOptionsType_Indirectly(t *testing.T) { + t.Parallel() + + var mockedService = new(TestExampleImplementation) + + mockedService.On("TheExampleMethodFunctionalOptions", "test", FunctionalOptions(OpNum(1), OpStr("foo"))).Return(nil).Once() + + tt := new(testing.T) + assert.False(t, mockedService.AssertExpectations(tt)) + + // make the call now + TheExampleMethodFunctionalOptionsIndirect(mockedService) + + // now assert expectations + assert.True(t, mockedService.AssertExpectations(tt)) + +} + +func Test_Mock_AssertExpectationsFunctionalOptionsType_Diff_Func(t *testing.T) { + t.Parallel() + + var mockedService = new(TestExampleImplementation) + + mockedService.On("TheExampleMethodFunctionalOptions", "test", FunctionalOptions(OpStr("this"))).Return(nil).Once() + + tt := new(testing.T) + assert.False(t, mockedService.AssertExpectations(tt)) + + assert.Panics(t, func() { + mockedService.TheExampleMethodFunctionalOptions("test", OpBytes([]byte("this"))) + }) +} + +func Test_Mock_AssertExpectationsFunctionalOptionsType_Diff_Arg(t *testing.T) { + t.Parallel() + + var mockedService = new(TestExampleImplementation) + + mockedService.On("TheExampleMethodFunctionalOptions", "test", FunctionalOptions(OpStr("this"))).Return(nil).Once() + + tt := new(testing.T) + assert.False(t, mockedService.AssertExpectations(tt)) + + assert.Panics(t, func() { + mockedService.TheExampleMethodFunctionalOptions("test", OpStr("that")) + }) +} + func Test_Mock_AssertExpectations_With_Repeatability(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1494,6 +1699,7 @@ func Test_Mock_AssertExpectations_With_Repeatability(t *testing.T) { } func Test_Mock_AssertExpectations_Skipped_Test(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1504,6 +1710,7 @@ func Test_Mock_AssertExpectations_Skipped_Test(t *testing.T) { } func Test_Mock_TwoCallsWithDifferentArguments(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1523,6 +1730,7 @@ func Test_Mock_TwoCallsWithDifferentArguments(t *testing.T) { } func Test_Mock_AssertNumberOfCalls(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1537,6 +1745,7 @@ func Test_Mock_AssertNumberOfCalls(t *testing.T) { } func Test_Mock_AssertCalled(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1549,6 +1758,7 @@ func Test_Mock_AssertCalled(t *testing.T) { } func Test_Mock_AssertCalled_WithAnythingOfTypeArgument(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1563,6 +1773,7 @@ func Test_Mock_AssertCalled_WithAnythingOfTypeArgument(t *testing.T) { } func Test_Mock_AssertCalled_WithArguments(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1577,6 +1788,7 @@ func Test_Mock_AssertCalled_WithArguments(t *testing.T) { } func Test_Mock_AssertCalled_WithArguments_With_Repeatability(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1594,6 +1806,7 @@ func Test_Mock_AssertCalled_WithArguments_With_Repeatability(t *testing.T) { } func Test_Mock_AssertNotCalled(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1606,6 +1819,8 @@ func Test_Mock_AssertNotCalled(t *testing.T) { } func Test_Mock_IsMethodCallable(t *testing.T) { + t.Parallel() + var mockedService = new(TestExampleImplementation) arg := []Call{{Repeatability: 1}, {Repeatability: 2}} @@ -1625,6 +1840,8 @@ func Test_Mock_IsMethodCallable(t *testing.T) { } func TestIsArgsEqual(t *testing.T) { + t.Parallel() + var expected = Arguments{5, 3, 4, 6, 7, 2} // Copy elements 1 to 5 @@ -1638,6 +1855,8 @@ func TestIsArgsEqual(t *testing.T) { } func Test_Mock_AssertOptional(t *testing.T) { + t.Parallel() + // Optional called var ms1 = new(TestExampleImplementation) ms1.On("TheExampleMethod", 1, 2, 3).Maybe().Return(4, nil) @@ -1666,6 +1885,7 @@ func Test_Mock_AssertOptional(t *testing.T) { Arguments helper methods */ func Test_Arguments_Get(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", 123, true}) @@ -1676,6 +1896,7 @@ func Test_Arguments_Get(t *testing.T) { } func Test_Arguments_Is(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", 123, true}) @@ -1685,6 +1906,7 @@ func Test_Arguments_Is(t *testing.T) { } func Test_Arguments_Diff(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"Hello World", 123, true}) var diff string @@ -1698,6 +1920,7 @@ func Test_Arguments_Diff(t *testing.T) { } func Test_Arguments_Diff_DifferentNumberOfArgs(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", 123, true}) var diff string @@ -1710,6 +1933,7 @@ func Test_Arguments_Diff_DifferentNumberOfArgs(t *testing.T) { } func Test_Arguments_Diff_WithAnythingArgument(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", 123, true}) var count int @@ -1720,6 +1944,7 @@ func Test_Arguments_Diff_WithAnythingArgument(t *testing.T) { } func Test_Arguments_Diff_WithAnythingArgument_InActualToo(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", Anything, true}) var count int @@ -1730,6 +1955,7 @@ func Test_Arguments_Diff_WithAnythingArgument_InActualToo(t *testing.T) { } func Test_Arguments_Diff_WithAnythingOfTypeArgument(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", AnythingOfType("int"), true}) var count int @@ -1740,6 +1966,7 @@ func Test_Arguments_Diff_WithAnythingOfTypeArgument(t *testing.T) { } func Test_Arguments_Diff_WithAnythingOfTypeArgument_Failing(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", AnythingOfType("string"), true}) var count int @@ -1752,6 +1979,8 @@ func Test_Arguments_Diff_WithAnythingOfTypeArgument_Failing(t *testing.T) { } func Test_Arguments_Diff_WithIsTypeArgument(t *testing.T) { + t.Parallel() + var args = Arguments([]interface{}{"string", IsType(0), true}) var count int _, count = args.Diff([]interface{}{"string", 123, true}) @@ -1760,6 +1989,8 @@ func Test_Arguments_Diff_WithIsTypeArgument(t *testing.T) { } func Test_Arguments_Diff_WithIsTypeArgument_Failing(t *testing.T) { + t.Parallel() + var args = Arguments([]interface{}{"string", IsType(""), true}) var count int var diff string @@ -1769,7 +2000,28 @@ func Test_Arguments_Diff_WithIsTypeArgument_Failing(t *testing.T) { assert.Contains(t, diff, `string != type int - (int=123)`) } +func Test_Arguments_Diff_WithIsTypeArgument_InterfaceType(t *testing.T) { + t.Parallel() + var ctx = context.Background() + args := Arguments([]interface{}{IsType(ctx)}) + _, count := args.Diff([]interface{}{context.Background()}) + assert.Equal(t, 0, count) +} + +func Test_Arguments_Diff_WithIsTypeArgument_InterfaceType_Failing(t *testing.T) { + t.Parallel() + + var ctx context.Context + var args = Arguments([]interface{}{IsType(ctx)}) + diff, count := args.Diff([]interface{}{context.Background()}) + assert.Equal(t, 1, count) + assert.Contains(t, diff, `type != type `) + +} + func Test_Arguments_Diff_WithArgMatcher(t *testing.T) { + t.Parallel() + matchFn := func(a int) bool { return a == 123 } @@ -1793,6 +2045,7 @@ func Test_Arguments_Diff_WithArgMatcher(t *testing.T) { } func Test_Arguments_Assert(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", 123, true}) @@ -1801,6 +2054,7 @@ func Test_Arguments_Assert(t *testing.T) { } func Test_Arguments_String_Representation(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", 123, true}) assert.Equal(t, `string,int,bool`, args.String()) @@ -1808,6 +2062,7 @@ func Test_Arguments_String_Representation(t *testing.T) { } func Test_Arguments_String(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", 123, true}) assert.Equal(t, "string", args.String(0)) @@ -1815,6 +2070,7 @@ func Test_Arguments_String(t *testing.T) { } func Test_Arguments_Error(t *testing.T) { + t.Parallel() var err = errors.New("An Error") var args = Arguments([]interface{}{"string", 123, true, err}) @@ -1823,6 +2079,7 @@ func Test_Arguments_Error(t *testing.T) { } func Test_Arguments_Error_Nil(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", 123, true, nil}) assert.Equal(t, nil, args.Error(3)) @@ -1830,6 +2087,7 @@ func Test_Arguments_Error_Nil(t *testing.T) { } func Test_Arguments_Int(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", 123, true}) assert.Equal(t, 123, args.Int(1)) @@ -1837,6 +2095,7 @@ func Test_Arguments_Int(t *testing.T) { } func Test_Arguments_Bool(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", 123, true}) assert.Equal(t, true, args.Bool(2)) @@ -1844,6 +2103,7 @@ func Test_Arguments_Bool(t *testing.T) { } func Test_WaitUntil_Parallel(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -1870,6 +2130,8 @@ func Test_WaitUntil_Parallel(t *testing.T) { } func Test_MockMethodCalled(t *testing.T) { + t.Parallel() + m := new(Mock) m.On("foo", "hello").Return("world") @@ -1880,6 +2142,8 @@ func Test_MockMethodCalled(t *testing.T) { } func Test_MockMethodCalled_Panic(t *testing.T) { + t.Parallel() + m := new(Mock) m.On("foo", "hello").Panic("world panics") @@ -1889,6 +2153,8 @@ func Test_MockMethodCalled_Panic(t *testing.T) { // Test to validate fix for racy concurrent call access in MethodCalled() func Test_MockReturnAndCalledConcurrent(t *testing.T) { + t.Parallel() + iterations := 1000 m := &Mock{} call := m.On("ConcurrencyTestMethod") @@ -1939,6 +2205,8 @@ func (tc *tCustomLogger) Errorf(format string, args ...interface{}) { func (tc *tCustomLogger) FailNow() {} func TestLoggingAssertExpectations(t *testing.T) { + t.Parallel() + m := new(timer) m.On("GetTime", 0).Return("") tcl := &tCustomLogger{t, []string{}, []string{}} @@ -1953,6 +2221,8 @@ func TestLoggingAssertExpectations(t *testing.T) { } func TestAfterTotalWaitTimeWhileExecution(t *testing.T) { + t.Parallel() + waitDuration := 1 total, waitMs := 5, time.Millisecond*time.Duration(waitDuration) aTimer := new(timer) @@ -1977,10 +2247,12 @@ func TestAfterTotalWaitTimeWhileExecution(t *testing.T) { } func TestArgumentMatcherToPrintMismatch(t *testing.T) { + t.Parallel() + defer func() { if r := recover(); r != nil { matchingExp := regexp.MustCompile( - `\s+mock: Unexpected Method Call\s+-*\s+GetTime\(int\)\s+0: 1\s+The closest call I have is:\s+GetTime\(mock.argumentMatcher\)\s+0: mock.argumentMatcher\{.*?\}\s+Diff:.*\(int=1\) not matched by func\(int\) bool`) + `\s+mock: Unexpected Method Call\s+-*\s+GetTime\(int\)\s+0: 1\s+The closest call I have is:\s+GetTime\(mock.argumentMatcher\)\s+0: mock.argumentMatcher\{.*?\}\s+Diff:.*\(int=1\) not matched by func\(int\) bool\nat: \[[^\]]+mock\/mock_test.go`) assert.Regexp(t, matchingExp, r) } }() @@ -1994,10 +2266,12 @@ func TestArgumentMatcherToPrintMismatch(t *testing.T) { } func TestArgumentMatcherToPrintMismatchWithReferenceType(t *testing.T) { + t.Parallel() + defer func() { if r := recover(); r != nil { matchingExp := regexp.MustCompile( - `\s+mock: Unexpected Method Call\s+-*\s+GetTimes\(\[\]int\)\s+0: \[\]int\{1\}\s+The closest call I have is:\s+GetTimes\(mock.argumentMatcher\)\s+0: mock.argumentMatcher\{.*?\}\s+Diff:.*\(\[\]int=\[1\]\) not matched by func\(\[\]int\) bool`) + `\s+mock: Unexpected Method Call\s+-*\s+GetTimes\(\[\]int\)\s+0: \[\]int\{1\}\s+The closest call I have is:\s+GetTimes\(mock.argumentMatcher\)\s+0: mock.argumentMatcher\{.*?\}\s+Diff:.*\(\[\]int=\[1\]\) not matched by func\(\[\]int\) bool\nat: \[[^\]]+mock\/mock_test.go`) assert.Regexp(t, matchingExp, r) } }() @@ -2011,6 +2285,8 @@ func TestArgumentMatcherToPrintMismatchWithReferenceType(t *testing.T) { } func TestClosestCallMismatchedArgumentInformationShowsTheClosest(t *testing.T) { + t.Parallel() + defer func() { if r := recover(); r != nil { matchingExp := regexp.MustCompile(unexpectedCallRegex(`TheExampleMethod(int,int,int)`, `0: 1\s+1: 1\s+2: 2`, `0: 1\s+1: 1\s+2: 1`, `Diff: 0: PASS: \(int=1\) == \(int=1\)\s+1: PASS: \(int=1\) == \(int=1\)\s+2: FAIL: \(int=2\) != \(int=1\)`)) @@ -2026,9 +2302,11 @@ func TestClosestCallMismatchedArgumentInformationShowsTheClosest(t *testing.T) { } func TestClosestCallFavorsFirstMock(t *testing.T) { + t.Parallel() + defer func() { if r := recover(); r != nil { - diffRegExp := `Difference found in argument 0:\s+--- Expected\s+\+\+\+ Actual\s+@@ -2,4 \+2,4 @@\s+\(bool\) true,\s+- \(bool\) true,\s+- \(bool\) true\s+\+ \(bool\) false,\s+\+ \(bool\) false\s+}\s+` + diffRegExp := `Difference found in argument 0:\s+--- Expected\s+\+\+\+ Actual\s+@@ -2,4 \+2,4 @@\s+\(bool\) true,\s+- \(bool\) true,\s+- \(bool\) true\s+\+ \(bool\) false,\s+\+ \(bool\) false\s+}\s+Diff: 0: FAIL: \(\[\]bool=\[(true\s?|false\s?){3}]\) != \(\[\]bool=\[(true\s?|false\s?){3}\]\)` matchingExp := regexp.MustCompile(unexpectedCallRegex(`TheExampleMethod7([]bool)`, `0: \[\]bool{true, false, false}`, `0: \[\]bool{true, true, true}`, diffRegExp)) assert.Regexp(t, matchingExp, r) } @@ -2042,9 +2320,11 @@ func TestClosestCallFavorsFirstMock(t *testing.T) { } func TestClosestCallUsesRepeatabilityToFindClosest(t *testing.T) { + t.Parallel() + defer func() { if r := recover(); r != nil { - diffRegExp := `Difference found in argument 0:\s+--- Expected\s+\+\+\+ Actual\s+@@ -1,4 \+1,4 @@\s+\(\[\]bool\) \(len=3\) {\s+- \(bool\) false,\s+- \(bool\) false,\s+\+ \(bool\) true,\s+\+ \(bool\) true,\s+\(bool\) false\s+` + diffRegExp := `Difference found in argument 0:\s+--- Expected\s+\+\+\+ Actual\s+@@ -1,4 \+1,4 @@\s+\(\[\]bool\) \(len=3\) {\s+- \(bool\) false,\s+- \(bool\) false,\s+\+ \(bool\) true,\s+\+ \(bool\) true,\s+\(bool\) false\s+Diff: 0: FAIL: \(\[\]bool=\[(true\s?|false\s?){3}]\) != \(\[\]bool=\[(true\s?|false\s?){3}\]\)` matchingExp := regexp.MustCompile(unexpectedCallRegex(`TheExampleMethod7([]bool)`, `0: \[\]bool{true, true, false}`, `0: \[\]bool{false, false, false}`, diffRegExp)) assert.Regexp(t, matchingExp, r) } @@ -2062,6 +2342,8 @@ func TestClosestCallUsesRepeatabilityToFindClosest(t *testing.T) { } func TestClosestCallMismatchedArgumentValueInformation(t *testing.T) { + t.Parallel() + defer func() { if r := recover(); r != nil { matchingExp := regexp.MustCompile(unexpectedCallRegex(`GetTime(int)`, "0: 1", "0: 999", `Diff: 0: FAIL: \(int=1\) != \(int=999\)`)) @@ -2076,32 +2358,44 @@ func TestClosestCallMismatchedArgumentValueInformation(t *testing.T) { } func Test_isBetterMatchThanReturnsFalseIfCandidateCallIsNil(t *testing.T) { + t.Parallel() + assert.False(t, matchCandidate{}.isBetterMatchThan(matchCandidate{})) } func Test_isBetterMatchThanReturnsTrueIfOtherCandidateCallIsNil(t *testing.T) { + t.Parallel() + assert.True(t, matchCandidate{call: &Call{}}.isBetterMatchThan(matchCandidate{})) } func Test_isBetterMatchThanReturnsFalseIfDiffCountIsGreaterThanOther(t *testing.T) { + t.Parallel() + assert.False(t, matchCandidate{call: &Call{}, diffCount: 2}.isBetterMatchThan(matchCandidate{call: &Call{}, diffCount: 1})) } func Test_isBetterMatchThanReturnsTrueIfDiffCountIsLessThanOther(t *testing.T) { + t.Parallel() + assert.True(t, matchCandidate{call: &Call{}, diffCount: 1}.isBetterMatchThan(matchCandidate{call: &Call{}, diffCount: 2})) } func Test_isBetterMatchThanReturnsTrueIfRepeatabilityIsGreaterThanOther(t *testing.T) { + t.Parallel() + assert.True(t, matchCandidate{call: &Call{Repeatability: 1}, diffCount: 1}.isBetterMatchThan(matchCandidate{call: &Call{Repeatability: -1}, diffCount: 1})) } func Test_isBetterMatchThanReturnsFalseIfRepeatabilityIsLessThanOrEqualToOther(t *testing.T) { + t.Parallel() + assert.False(t, matchCandidate{call: &Call{Repeatability: 1}, diffCount: 1}.isBetterMatchThan(matchCandidate{call: &Call{Repeatability: 1}, diffCount: 1})) } func unexpectedCallRegex(method, calledArg, expectedArg, diff string) string { rMethod := regexp.QuoteMeta(method) - return fmt.Sprintf(`\s+mock: Unexpected Method Call\s+-*\s+%s\s+%s\s+The closest call I have is:\s+%s\s+%s\s+%s`, + return fmt.Sprintf(`\s+mock: Unexpected Method Call\s+-*\s+%s\s+%s\s+The closest call I have is:\s+%s\s+%s\s+%s\nat: \[[^\]]+mock\/mock_test.go`, rMethod, calledArg, rMethod, expectedArg, diff) } @@ -2111,6 +2405,8 @@ func ConcurrencyTestMethod(m *Mock) { } func TestConcurrentArgumentRead(t *testing.T) { + t.Parallel() + methodUnderTest := func(c caller, u user) { go u.Use(c) c.Call() @@ -2146,3 +2442,28 @@ type user interface { type mockUser struct{ Mock } func (m *mockUser) Use(c caller) { m.Called(c) } + +type mutatingStringer struct { + N int + s string +} + +func (m *mutatingStringer) String() string { + m.s = strconv.Itoa(m.N) + return m.s +} + +func TestIssue1785ArgumentWithMutatingStringer(t *testing.T) { + m := &Mock{} + m.On("Method", &mutatingStringer{N: 2}) + m.On("Method", &mutatingStringer{N: 1}) + m.MethodCalled("Method", &mutatingStringer{N: 1}) + m.MethodCalled("Method", &mutatingStringer{N: 2}) + m.AssertExpectations(t) +} + +func TestIssue1227AssertExpectationsForObjectsWithMock(t *testing.T) { + mockT := &MockTestingT{} + AssertExpectationsForObjects(mockT, Mock{}) + assert.Equal(t, 1, mockT.errorfCount) +} diff --git a/require/doc.go b/require/doc.go index 968434724..ee84cc479 100644 --- a/require/doc.go +++ b/require/doc.go @@ -1,4 +1,4 @@ -// Package require implements the same assertions as the `assert` package but +// Package require implements the same assertions as the assert package but // stops test execution when a test fails. // // # Example Usage @@ -21,8 +21,10 @@ // // # Assertions // -// The `require` package have same global functions as in the `assert` package, -// but instead of returning a boolean result they call `t.FailNow()`. +// The require package have same global functions as in the assert package, +// but instead of returning a boolean result they call [testing.T.FailNow]. +// A consequence of this is that it must be called from the goroutine running +// the test function, not from other goroutines created during the test. // // Every assertion function also takes an optional string message as the final argument, // allowing custom error messages to be appended to the message the assertion method outputs. diff --git a/require/forward_requirements_test.go b/require/forward_requirements_test.go index 8fbcc1530..617bfb2c3 100644 --- a/require/forward_requirements_test.go +++ b/require/forward_requirements_test.go @@ -7,6 +7,8 @@ import ( ) func TestImplementsWrapper(t *testing.T) { + t.Parallel() + require := New(t) require.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject)) @@ -19,7 +21,23 @@ func TestImplementsWrapper(t *testing.T) { } } +func TestIsNotTypeWrapper(t *testing.T) { + t.Parallel() + + require := New(t) + require.IsNotType(new(AssertionTesterNonConformingObject), new(AssertionTesterConformingObject)) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.IsNotType(new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) + if !mockT.Failed { + t.Error("Check should fail") + } +} + func TestIsTypeWrapper(t *testing.T) { + t.Parallel() + require := New(t) require.IsType(new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) @@ -32,6 +50,8 @@ func TestIsTypeWrapper(t *testing.T) { } func TestEqualWrapper(t *testing.T) { + t.Parallel() + require := New(t) require.Equal(1, 1) @@ -44,6 +64,8 @@ func TestEqualWrapper(t *testing.T) { } func TestNotEqualWrapper(t *testing.T) { + t.Parallel() + require := New(t) require.NotEqual(1, 2) @@ -56,6 +78,8 @@ func TestNotEqualWrapper(t *testing.T) { } func TestExactlyWrapper(t *testing.T) { + t.Parallel() + require := New(t) a := float32(1) @@ -73,6 +97,8 @@ func TestExactlyWrapper(t *testing.T) { } func TestNotNilWrapper(t *testing.T) { + t.Parallel() + require := New(t) require.NotNil(t, new(AssertionTesterConformingObject)) @@ -85,6 +111,8 @@ func TestNotNilWrapper(t *testing.T) { } func TestNilWrapper(t *testing.T) { + t.Parallel() + require := New(t) require.Nil(nil) @@ -97,6 +125,8 @@ func TestNilWrapper(t *testing.T) { } func TestTrueWrapper(t *testing.T) { + t.Parallel() + require := New(t) require.True(true) @@ -109,6 +139,8 @@ func TestTrueWrapper(t *testing.T) { } func TestFalseWrapper(t *testing.T) { + t.Parallel() + require := New(t) require.False(false) @@ -121,6 +153,8 @@ func TestFalseWrapper(t *testing.T) { } func TestContainsWrapper(t *testing.T) { + t.Parallel() + require := New(t) require.Contains("Hello World", "Hello") @@ -133,6 +167,8 @@ func TestContainsWrapper(t *testing.T) { } func TestNotContainsWrapper(t *testing.T) { + t.Parallel() + require := New(t) require.NotContains("Hello World", "Hello!") @@ -145,6 +181,8 @@ func TestNotContainsWrapper(t *testing.T) { } func TestPanicsWrapper(t *testing.T) { + t.Parallel() + require := New(t) require.Panics(func() { panic("Panic!") @@ -159,6 +197,8 @@ func TestPanicsWrapper(t *testing.T) { } func TestNotPanicsWrapper(t *testing.T) { + t.Parallel() + require := New(t) require.NotPanics(func() {}) @@ -173,6 +213,8 @@ func TestNotPanicsWrapper(t *testing.T) { } func TestNoErrorWrapper(t *testing.T) { + t.Parallel() + require := New(t) require.NoError(nil) @@ -185,6 +227,8 @@ func TestNoErrorWrapper(t *testing.T) { } func TestErrorWrapper(t *testing.T) { + t.Parallel() + require := New(t) require.Error(errors.New("some error")) @@ -197,6 +241,8 @@ func TestErrorWrapper(t *testing.T) { } func TestErrorContainsWrapper(t *testing.T) { + t.Parallel() + require := New(t) require.ErrorContains(errors.New("some error: another error"), "some error") @@ -209,6 +255,8 @@ func TestErrorContainsWrapper(t *testing.T) { } func TestEqualErrorWrapper(t *testing.T) { + t.Parallel() + require := New(t) require.EqualError(errors.New("some error"), "some error") @@ -221,6 +269,8 @@ func TestEqualErrorWrapper(t *testing.T) { } func TestEmptyWrapper(t *testing.T) { + t.Parallel() + require := New(t) require.Empty("") @@ -233,6 +283,8 @@ func TestEmptyWrapper(t *testing.T) { } func TestNotEmptyWrapper(t *testing.T) { + t.Parallel() + require := New(t) require.NotEmpty("x") @@ -245,6 +297,8 @@ func TestNotEmptyWrapper(t *testing.T) { } func TestWithinDurationWrapper(t *testing.T) { + t.Parallel() + require := New(t) a := time.Now() b := a.Add(10 * time.Second) @@ -260,6 +314,8 @@ func TestWithinDurationWrapper(t *testing.T) { } func TestInDeltaWrapper(t *testing.T) { + t.Parallel() + require := New(t) require.InDelta(1.001, 1, 0.01) @@ -272,6 +328,8 @@ func TestInDeltaWrapper(t *testing.T) { } func TestZeroWrapper(t *testing.T) { + t.Parallel() + require := New(t) require.Zero(0) @@ -284,6 +342,8 @@ func TestZeroWrapper(t *testing.T) { } func TestNotZeroWrapper(t *testing.T) { + t.Parallel() + require := New(t) require.NotZero(1) @@ -296,6 +356,8 @@ func TestNotZeroWrapper(t *testing.T) { } func TestJSONEqWrapper_EqualSONString(t *testing.T) { + t.Parallel() + mockT := new(MockT) mockRequire := New(mockT) @@ -306,6 +368,8 @@ func TestJSONEqWrapper_EqualSONString(t *testing.T) { } func TestJSONEqWrapper_EquivalentButNotEqual(t *testing.T) { + t.Parallel() + mockT := new(MockT) mockRequire := New(mockT) @@ -316,6 +380,8 @@ func TestJSONEqWrapper_EquivalentButNotEqual(t *testing.T) { } func TestJSONEqWrapper_HashOfArraysAndHashes(t *testing.T) { + t.Parallel() + mockT := new(MockT) mockRequire := New(mockT) @@ -327,6 +393,8 @@ func TestJSONEqWrapper_HashOfArraysAndHashes(t *testing.T) { } func TestJSONEqWrapper_Array(t *testing.T) { + t.Parallel() + mockT := new(MockT) mockRequire := New(mockT) @@ -337,6 +405,8 @@ func TestJSONEqWrapper_Array(t *testing.T) { } func TestJSONEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { + t.Parallel() + mockT := new(MockT) mockRequire := New(mockT) @@ -347,6 +417,8 @@ func TestJSONEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { } func TestJSONEqWrapper_HashesNotEquivalent(t *testing.T) { + t.Parallel() + mockT := new(MockT) mockRequire := New(mockT) @@ -357,6 +429,8 @@ func TestJSONEqWrapper_HashesNotEquivalent(t *testing.T) { } func TestJSONEqWrapper_ActualIsNotJSON(t *testing.T) { + t.Parallel() + mockT := new(MockT) mockRequire := New(mockT) @@ -367,6 +441,8 @@ func TestJSONEqWrapper_ActualIsNotJSON(t *testing.T) { } func TestJSONEqWrapper_ExpectedIsNotJSON(t *testing.T) { + t.Parallel() + mockT := new(MockT) mockRequire := New(mockT) @@ -377,6 +453,8 @@ func TestJSONEqWrapper_ExpectedIsNotJSON(t *testing.T) { } func TestJSONEqWrapper_ExpectedAndActualNotJSON(t *testing.T) { + t.Parallel() + mockT := new(MockT) mockRequire := New(mockT) @@ -387,6 +465,8 @@ func TestJSONEqWrapper_ExpectedAndActualNotJSON(t *testing.T) { } func TestJSONEqWrapper_ArraysOfDifferentOrder(t *testing.T) { + t.Parallel() + mockT := new(MockT) mockRequire := New(mockT) @@ -397,6 +477,8 @@ func TestJSONEqWrapper_ArraysOfDifferentOrder(t *testing.T) { } func TestYAMLEqWrapper_EqualYAMLString(t *testing.T) { + t.Parallel() + mockT := new(MockT) mockRequire := New(mockT) @@ -407,6 +489,8 @@ func TestYAMLEqWrapper_EqualYAMLString(t *testing.T) { } func TestYAMLEqWrapper_EquivalentButNotEqual(t *testing.T) { + t.Parallel() + mockT := new(MockT) mockRequire := New(mockT) @@ -417,6 +501,8 @@ func TestYAMLEqWrapper_EquivalentButNotEqual(t *testing.T) { } func TestYAMLEqWrapper_HashOfArraysAndHashes(t *testing.T) { + t.Parallel() + mockT := new(MockT) mockRequire := New(mockT) @@ -453,6 +539,8 @@ array: } func TestYAMLEqWrapper_Array(t *testing.T) { + t.Parallel() + mockT := new(MockT) mockRequire := New(mockT) @@ -463,6 +551,8 @@ func TestYAMLEqWrapper_Array(t *testing.T) { } func TestYAMLEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { + t.Parallel() + mockT := new(MockT) mockRequire := New(mockT) @@ -473,6 +563,8 @@ func TestYAMLEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { } func TestYAMLEqWrapper_HashesNotEquivalent(t *testing.T) { + t.Parallel() + mockT := new(MockT) mockRequire := New(mockT) @@ -483,6 +575,8 @@ func TestYAMLEqWrapper_HashesNotEquivalent(t *testing.T) { } func TestYAMLEqWrapper_ActualIsSimpleString(t *testing.T) { + t.Parallel() + mockT := new(MockT) mockRequire := New(mockT) @@ -493,6 +587,8 @@ func TestYAMLEqWrapper_ActualIsSimpleString(t *testing.T) { } func TestYAMLEqWrapper_ExpectedIsSimpleString(t *testing.T) { + t.Parallel() + mockT := new(MockT) mockRequire := New(mockT) @@ -503,6 +599,8 @@ func TestYAMLEqWrapper_ExpectedIsSimpleString(t *testing.T) { } func TestYAMLEqWrapper_ExpectedAndActualSimpleString(t *testing.T) { + t.Parallel() + mockT := new(MockT) mockRequire := New(mockT) @@ -513,6 +611,8 @@ func TestYAMLEqWrapper_ExpectedAndActualSimpleString(t *testing.T) { } func TestYAMLEqWrapper_ArraysOfDifferentOrder(t *testing.T) { + t.Parallel() + mockT := new(MockT) mockRequire := New(mockT) diff --git a/require/require.go b/require/require.go index d0c73ff13..23a3be780 100644 --- a/require/require.go +++ b/require/require.go @@ -34,9 +34,9 @@ func Conditionf(t TestingT, comp assert.Comparison, msg string, args ...interfac // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// assert.Contains(t, "Hello World", "World") -// assert.Contains(t, ["Hello", "World"], "World") -// assert.Contains(t, {"Hello": "World"}, "Hello") +// require.Contains(t, "Hello World", "World") +// require.Contains(t, ["Hello", "World"], "World") +// require.Contains(t, {"Hello": "World"}, "Hello") func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -50,9 +50,9 @@ func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...int // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") -// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") -// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") +// require.Containsf(t, "Hello World", "World", "error message %s", "formatted") +// require.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") +// require.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -91,7 +91,7 @@ func DirExistsf(t TestingT, path string, msg string, args ...interface{}) { // listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, // the number of appearances of each of them in both lists should match. // -// assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) +// require.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) func ElementsMatch(t TestingT, listA interface{}, listB interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -106,7 +106,7 @@ func ElementsMatch(t TestingT, listA interface{}, listB interface{}, msgAndArgs // listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, // the number of appearances of each of them in both lists should match. // -// assert.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") +// require.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -117,10 +117,19 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string t.FailNow() } -// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Empty asserts that the given value is "empty". // -// assert.Empty(t, obj) +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". +// +// require.Empty(t, obj) +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -131,10 +140,19 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { t.FailNow() } -// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Emptyf asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). // -// assert.Emptyf(t, obj, "error message %s", "formatted") +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". +// +// require.Emptyf(t, obj, "error message %s", "formatted") +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -147,7 +165,7 @@ func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { // Equal asserts that two objects are equal. // -// assert.Equal(t, 123, 123) +// require.Equal(t, 123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -162,11 +180,11 @@ func Equal(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...i t.FailNow() } -// EqualError asserts that a function returned an error (i.e. not `nil`) +// EqualError asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() -// assert.EqualError(t, err, expectedErrorString) +// require.EqualError(t, err, expectedErrorString) func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -177,11 +195,11 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte t.FailNow() } -// EqualErrorf asserts that a function returned an error (i.e. not `nil`) +// EqualErrorf asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() -// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") +// require.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -200,8 +218,8 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args // Exported int // notExported int // } -// assert.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true -// assert.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false +// require.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true +// require.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false func EqualExportedValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -220,8 +238,8 @@ func EqualExportedValues(t TestingT, expected interface{}, actual interface{}, m // Exported int // notExported int // } -// assert.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true -// assert.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false +// require.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true +// require.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -235,7 +253,7 @@ func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, // EqualValues asserts that two objects are equal or convertible to the larger // type and equal. // -// assert.EqualValues(t, uint32(123), int32(123)) +// require.EqualValues(t, uint32(123), int32(123)) func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -249,7 +267,7 @@ func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArg // EqualValuesf asserts that two objects are equal or convertible to the larger // type and equal. // -// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") +// require.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -262,7 +280,7 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri // Equalf asserts that two objects are equal. // -// assert.Equalf(t, 123, 123, "error message %s", "formatted") +// require.Equalf(t, 123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -277,12 +295,10 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar t.FailNow() } -// Error asserts that a function returned an error (i.e. not `nil`). +// Error asserts that a function returned a non-nil error (ie. an error). // -// actualObj, err := SomeFunction() -// if assert.Error(t, err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// require.Error(t, err) func Error(t TestingT, err error, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -317,11 +333,11 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int t.FailNow() } -// ErrorContains asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContains asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() -// assert.ErrorContains(t, err, expectedErrorSubString) +// require.ErrorContains(t, err, expectedErrorSubString) func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -332,11 +348,11 @@ func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...in t.FailNow() } -// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContainsf asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() -// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") +// require.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -371,12 +387,10 @@ func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface t.FailNow() } -// Errorf asserts that a function returned an error (i.e. not `nil`). +// Errorf asserts that a function returned a non-nil error (ie. an error). // -// actualObj, err := SomeFunction() -// if assert.Errorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// require.Errorf(t, err, "error message %s", "formatted") func Errorf(t TestingT, err error, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -390,7 +404,7 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) { // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) +// require.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -415,9 +429,9 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t // time.Sleep(8*time.Second) // externalValue = true // }() -// assert.EventuallyWithT(t, func(c *assert.CollectT) { +// require.EventuallyWithT(t, func(c *require.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick -// assert.True(c, externalValue, "expected 'externalValue' to be true") +// require.True(c, externalValue, "expected 'externalValue' to be true") // }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { @@ -443,9 +457,9 @@ func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT), waitF // time.Sleep(8*time.Second) // externalValue = true // }() -// assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") { +// require.EventuallyWithTf(t, func(c *require.CollectT, "error message %s", "formatted") { // // add assertions as needed; any assertion failure will fail the current tick -// assert.True(c, externalValue, "expected 'externalValue' to be true") +// require.True(c, externalValue, "expected 'externalValue' to be true") // }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") func EventuallyWithTf(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { @@ -460,7 +474,7 @@ func EventuallyWithTf(t TestingT, condition func(collect *assert.CollectT), wait // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// require.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -473,7 +487,7 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick // Exactly asserts that two objects are equal in value and type. // -// assert.Exactly(t, int32(123), int64(123)) +// require.Exactly(t, int32(123), int64(123)) func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -486,7 +500,7 @@ func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs .. // Exactlyf asserts that two objects are equal in value and type. // -// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") +// require.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -543,7 +557,7 @@ func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) { // False asserts that the specified value is false. // -// assert.False(t, myBool) +// require.False(t, myBool) func False(t TestingT, value bool, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -556,7 +570,7 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) { // Falsef asserts that the specified value is false. // -// assert.Falsef(t, myBool, "error message %s", "formatted") +// require.Falsef(t, myBool, "error message %s", "formatted") func Falsef(t TestingT, value bool, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -593,9 +607,9 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) { // Greater asserts that the first element is greater than the second // -// assert.Greater(t, 2, 1) -// assert.Greater(t, float64(2), float64(1)) -// assert.Greater(t, "b", "a") +// require.Greater(t, 2, 1) +// require.Greater(t, float64(2), float64(1)) +// require.Greater(t, "b", "a") func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -608,10 +622,10 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface // GreaterOrEqual asserts that the first element is greater than or equal to the second // -// assert.GreaterOrEqual(t, 2, 1) -// assert.GreaterOrEqual(t, 2, 2) -// assert.GreaterOrEqual(t, "b", "a") -// assert.GreaterOrEqual(t, "b", "b") +// require.GreaterOrEqual(t, 2, 1) +// require.GreaterOrEqual(t, 2, 2) +// require.GreaterOrEqual(t, "b", "a") +// require.GreaterOrEqual(t, "b", "b") func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -624,10 +638,10 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in // GreaterOrEqualf asserts that the first element is greater than or equal to the second // -// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") +// require.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") +// require.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") +// require.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") +// require.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -640,9 +654,9 @@ func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, arg // Greaterf asserts that the first element is greater than the second // -// assert.Greaterf(t, 2, 1, "error message %s", "formatted") -// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") -// assert.Greaterf(t, "b", "a", "error message %s", "formatted") +// require.Greaterf(t, 2, 1, "error message %s", "formatted") +// require.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") +// require.Greaterf(t, "b", "a", "error message %s", "formatted") func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -656,7 +670,7 @@ func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...in // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// require.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { @@ -672,7 +686,7 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url s // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// require.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { @@ -688,7 +702,7 @@ func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// require.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { @@ -704,7 +718,7 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, ur // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// require.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { @@ -719,7 +733,7 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u // HTTPError asserts that a specified handler returns an error status code. // -// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// require.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -734,7 +748,7 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, // HTTPErrorf asserts that a specified handler returns an error status code. // -// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// require.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -749,7 +763,7 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, // HTTPRedirect asserts that a specified handler returns a redirect status code. // -// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// require.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -764,7 +778,7 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url strin // HTTPRedirectf asserts that a specified handler returns a redirect status code. // -// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// require.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -779,7 +793,7 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri // HTTPStatusCode asserts that a specified handler returns a specified status code. // -// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) +// require.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { @@ -794,7 +808,7 @@ func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url str // HTTPStatusCodef asserts that a specified handler returns a specified status code. // -// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// require.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { @@ -809,7 +823,7 @@ func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url st // HTTPSuccess asserts that a specified handler returns a success status code. // -// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) +// require.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -824,7 +838,7 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string // HTTPSuccessf asserts that a specified handler returns a success status code. // -// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// require.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -839,7 +853,7 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin // Implements asserts that an object is implemented by the specified interface. // -// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) +// require.Implements(t, (*MyInterface)(nil), new(MyObject)) func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -852,7 +866,7 @@ func Implements(t TestingT, interfaceObject interface{}, object interface{}, msg // Implementsf asserts that an object is implemented by the specified interface. // -// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +// require.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -865,7 +879,7 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms // InDelta asserts that the two numerals are within delta of each other. // -// assert.InDelta(t, math.Pi, 22/7.0, 0.01) +// require.InDelta(t, math.Pi, 22/7.0, 0.01) func InDelta(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -922,7 +936,7 @@ func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta f // InDeltaf asserts that the two numerals are within delta of each other. // -// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +// require.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -979,9 +993,9 @@ func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon fl // IsDecreasing asserts that the collection is decreasing // -// assert.IsDecreasing(t, []int{2, 1, 0}) -// assert.IsDecreasing(t, []float{2, 1}) -// assert.IsDecreasing(t, []string{"b", "a"}) +// require.IsDecreasing(t, []int{2, 1, 0}) +// require.IsDecreasing(t, []float{2, 1}) +// require.IsDecreasing(t, []string{"b", "a"}) func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -994,9 +1008,9 @@ func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { // IsDecreasingf asserts that the collection is decreasing // -// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +// require.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") +// require.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") +// require.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1009,9 +1023,9 @@ func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface // IsIncreasing asserts that the collection is increasing // -// assert.IsIncreasing(t, []int{1, 2, 3}) -// assert.IsIncreasing(t, []float{1, 2}) -// assert.IsIncreasing(t, []string{"a", "b"}) +// require.IsIncreasing(t, []int{1, 2, 3}) +// require.IsIncreasing(t, []float{1, 2}) +// require.IsIncreasing(t, []string{"a", "b"}) func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1024,9 +1038,9 @@ func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { // IsIncreasingf asserts that the collection is increasing // -// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +// require.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") +// require.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") +// require.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1039,9 +1053,9 @@ func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface // IsNonDecreasing asserts that the collection is not decreasing // -// assert.IsNonDecreasing(t, []int{1, 1, 2}) -// assert.IsNonDecreasing(t, []float{1, 2}) -// assert.IsNonDecreasing(t, []string{"a", "b"}) +// require.IsNonDecreasing(t, []int{1, 1, 2}) +// require.IsNonDecreasing(t, []float{1, 2}) +// require.IsNonDecreasing(t, []string{"a", "b"}) func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1054,9 +1068,9 @@ func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) // IsNonDecreasingf asserts that the collection is not decreasing // -// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +// require.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") +// require.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") +// require.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1069,9 +1083,9 @@ func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interf // IsNonIncreasing asserts that the collection is not increasing // -// assert.IsNonIncreasing(t, []int{2, 1, 1}) -// assert.IsNonIncreasing(t, []float{2, 1}) -// assert.IsNonIncreasing(t, []string{"b", "a"}) +// require.IsNonIncreasing(t, []int{2, 1, 1}) +// require.IsNonIncreasing(t, []float{2, 1}) +// require.IsNonIncreasing(t, []string{"b", "a"}) func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1084,9 +1098,9 @@ func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) // IsNonIncreasingf asserts that the collection is not increasing // -// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +// require.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") +// require.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") +// require.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1097,7 +1111,35 @@ func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interf t.FailNow() } +// IsNotType asserts that the specified objects are not of the same type. +// +// require.IsNotType(t, &NotMyStruct{}, &MyStruct{}) +func IsNotType(t TestingT, theType interface{}, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsNotType(t, theType, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// IsNotTypef asserts that the specified objects are not of the same type. +// +// require.IsNotTypef(t, &NotMyStruct{}, &MyStruct{}, "error message %s", "formatted") +func IsNotTypef(t TestingT, theType interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsNotTypef(t, theType, object, msg, args...) { + return + } + t.FailNow() +} + // IsType asserts that the specified objects are of the same type. +// +// require.IsType(t, &MyStruct{}, &MyStruct{}) func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1109,6 +1151,8 @@ func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs } // IsTypef asserts that the specified objects are of the same type. +// +// require.IsTypef(t, &MyStruct{}, &MyStruct{}, "error message %s", "formatted") func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1121,7 +1165,7 @@ func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg strin // JSONEq asserts that two JSON strings are equivalent. // -// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// require.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1134,7 +1178,7 @@ func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{ // JSONEqf asserts that two JSON strings are equivalent. // -// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +// require.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1148,7 +1192,7 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // -// assert.Len(t, mySlice, 3) +// require.Len(t, mySlice, 3) func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1162,7 +1206,7 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // -// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") +// require.Lenf(t, mySlice, 3, "error message %s", "formatted") func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1175,9 +1219,9 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf // Less asserts that the first element is less than the second // -// assert.Less(t, 1, 2) -// assert.Less(t, float64(1), float64(2)) -// assert.Less(t, "a", "b") +// require.Less(t, 1, 2) +// require.Less(t, float64(1), float64(2)) +// require.Less(t, "a", "b") func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1190,10 +1234,10 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) // LessOrEqual asserts that the first element is less than or equal to the second // -// assert.LessOrEqual(t, 1, 2) -// assert.LessOrEqual(t, 2, 2) -// assert.LessOrEqual(t, "a", "b") -// assert.LessOrEqual(t, "b", "b") +// require.LessOrEqual(t, 1, 2) +// require.LessOrEqual(t, 2, 2) +// require.LessOrEqual(t, "a", "b") +// require.LessOrEqual(t, "b", "b") func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1206,10 +1250,10 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter // LessOrEqualf asserts that the first element is less than or equal to the second // -// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") -// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") +// require.LessOrEqualf(t, 1, 2, "error message %s", "formatted") +// require.LessOrEqualf(t, 2, 2, "error message %s", "formatted") +// require.LessOrEqualf(t, "a", "b", "error message %s", "formatted") +// require.LessOrEqualf(t, "b", "b", "error message %s", "formatted") func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1222,9 +1266,9 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args . // Lessf asserts that the first element is less than the second // -// assert.Lessf(t, 1, 2, "error message %s", "formatted") -// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") -// assert.Lessf(t, "a", "b", "error message %s", "formatted") +// require.Lessf(t, 1, 2, "error message %s", "formatted") +// require.Lessf(t, float64(1), float64(2), "error message %s", "formatted") +// require.Lessf(t, "a", "b", "error message %s", "formatted") func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1237,8 +1281,8 @@ func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...inter // Negative asserts that the specified element is negative // -// assert.Negative(t, -1) -// assert.Negative(t, -1.23) +// require.Negative(t, -1) +// require.Negative(t, -1.23) func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1251,8 +1295,8 @@ func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) { // Negativef asserts that the specified element is negative // -// assert.Negativef(t, -1, "error message %s", "formatted") -// assert.Negativef(t, -1.23, "error message %s", "formatted") +// require.Negativef(t, -1, "error message %s", "formatted") +// require.Negativef(t, -1.23, "error message %s", "formatted") func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1266,7 +1310,7 @@ func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) { // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) +// require.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1280,7 +1324,7 @@ func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.D // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// require.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1293,7 +1337,7 @@ func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time. // Nil asserts that the specified object is nil. // -// assert.Nil(t, err) +// require.Nil(t, err) func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1306,7 +1350,7 @@ func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) { // Nilf asserts that the specified object is nil. // -// assert.Nilf(t, err, "error message %s", "formatted") +// require.Nilf(t, err, "error message %s", "formatted") func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1341,11 +1385,11 @@ func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) { t.FailNow() } -// NoError asserts that a function returned no error (i.e. `nil`). +// NoError asserts that a function returned a nil error (ie. no error). // // actualObj, err := SomeFunction() -// if assert.NoError(t, err) { -// assert.Equal(t, expectedObj, actualObj) +// if require.NoError(t, err) { +// require.Equal(t, expectedObj, actualObj) // } func NoError(t TestingT, err error, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { @@ -1357,11 +1401,11 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) { t.FailNow() } -// NoErrorf asserts that a function returned no error (i.e. `nil`). +// NoErrorf asserts that a function returned a nil error (ie. no error). // // actualObj, err := SomeFunction() -// if assert.NoErrorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) +// if require.NoErrorf(t, err, "error message %s", "formatted") { +// require.Equal(t, expectedObj, actualObj) // } func NoErrorf(t TestingT, err error, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { @@ -1400,9 +1444,9 @@ func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) { // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// assert.NotContains(t, "Hello World", "Earth") -// assert.NotContains(t, ["Hello", "World"], "Earth") -// assert.NotContains(t, {"Hello": "World"}, "Earth") +// require.NotContains(t, "Hello World", "Earth") +// require.NotContains(t, ["Hello", "World"], "Earth") +// require.NotContains(t, {"Hello": "World"}, "Earth") func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1416,9 +1460,9 @@ func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ... // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") +// require.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") +// require.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") +// require.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1434,11 +1478,11 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a // the number of appearances of each of them in both lists should not match. // This is an inverse of ElementsMatch. // -// assert.NotElementsMatch(t, [1, 1, 2, 3], [1, 1, 2, 3]) -> false +// require.NotElementsMatch(t, [1, 1, 2, 3], [1, 1, 2, 3]) -> false // -// assert.NotElementsMatch(t, [1, 1, 2, 3], [1, 2, 3]) -> true +// require.NotElementsMatch(t, [1, 1, 2, 3], [1, 2, 3]) -> true // -// assert.NotElementsMatch(t, [1, 2, 3], [1, 2, 4]) -> true +// require.NotElementsMatch(t, [1, 2, 3], [1, 2, 4]) -> true func NotElementsMatch(t TestingT, listA interface{}, listB interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1454,11 +1498,11 @@ func NotElementsMatch(t TestingT, listA interface{}, listB interface{}, msgAndAr // the number of appearances of each of them in both lists should not match. // This is an inverse of ElementsMatch. // -// assert.NotElementsMatchf(t, [1, 1, 2, 3], [1, 1, 2, 3], "error message %s", "formatted") -> false +// require.NotElementsMatchf(t, [1, 1, 2, 3], [1, 1, 2, 3], "error message %s", "formatted") -> false // -// assert.NotElementsMatchf(t, [1, 1, 2, 3], [1, 2, 3], "error message %s", "formatted") -> true +// require.NotElementsMatchf(t, [1, 1, 2, 3], [1, 2, 3], "error message %s", "formatted") -> true // -// assert.NotElementsMatchf(t, [1, 2, 3], [1, 2, 4], "error message %s", "formatted") -> true +// require.NotElementsMatchf(t, [1, 2, 3], [1, 2, 4], "error message %s", "formatted") -> true func NotElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1469,11 +1513,10 @@ func NotElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg str t.FailNow() } -// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmpty asserts that the specified object is NOT [Empty]. // -// if assert.NotEmpty(t, obj) { -// assert.Equal(t, "two", obj[1]) +// if require.NotEmpty(t, obj) { +// require.Equal(t, "two", obj[1]) // } func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { @@ -1485,11 +1528,10 @@ func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { t.FailNow() } -// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmptyf asserts that the specified object is NOT [Empty]. // -// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) +// if require.NotEmptyf(t, obj, "error message %s", "formatted") { +// require.Equal(t, "two", obj[1]) // } func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { @@ -1503,7 +1545,7 @@ func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) // NotEqual asserts that the specified values are NOT equal. // -// assert.NotEqual(t, obj1, obj2) +// require.NotEqual(t, obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1519,7 +1561,7 @@ func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs . // NotEqualValues asserts that two objects are not equal even when converted to the same type // -// assert.NotEqualValues(t, obj1, obj2) +// require.NotEqualValues(t, obj1, obj2) func NotEqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1532,7 +1574,7 @@ func NotEqualValues(t TestingT, expected interface{}, actual interface{}, msgAnd // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // -// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") +// require.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1545,7 +1587,7 @@ func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg s // NotEqualf asserts that the specified values are NOT equal. // -// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") +// require.NotEqualf(t, obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1559,6 +1601,30 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, t.FailNow() } +// NotErrorAs asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func NotErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotErrorAs(t, err, target, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotErrorAsf asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func NotErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotErrorAsf(t, err, target, msg, args...) { + return + } + t.FailNow() +} + // NotErrorIs asserts that none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func NotErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) { @@ -1585,7 +1651,7 @@ func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interf // NotImplements asserts that an object does not implement the specified interface. // -// assert.NotImplements(t, (*MyInterface)(nil), new(MyObject)) +// require.NotImplements(t, (*MyInterface)(nil), new(MyObject)) func NotImplements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1598,7 +1664,7 @@ func NotImplements(t TestingT, interfaceObject interface{}, object interface{}, // NotImplementsf asserts that an object does not implement the specified interface. // -// assert.NotImplementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +// require.NotImplementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func NotImplementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1611,7 +1677,7 @@ func NotImplementsf(t TestingT, interfaceObject interface{}, object interface{}, // NotNil asserts that the specified object is not nil. // -// assert.NotNil(t, err) +// require.NotNil(t, err) func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1624,7 +1690,7 @@ func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) { // NotNilf asserts that the specified object is not nil. // -// assert.NotNilf(t, err, "error message %s", "formatted") +// require.NotNilf(t, err, "error message %s", "formatted") func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1637,7 +1703,7 @@ func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) { // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // -// assert.NotPanics(t, func(){ RemainCalm() }) +// require.NotPanics(t, func(){ RemainCalm() }) func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1650,7 +1716,7 @@ func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // -// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") +// require.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") func NotPanicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1663,8 +1729,8 @@ func NotPanicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interfac // NotRegexp asserts that a specified regexp does not match a string. // -// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") -// assert.NotRegexp(t, "^start", "it's not starting") +// require.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") +// require.NotRegexp(t, "^start", "it's not starting") func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1677,8 +1743,8 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf // NotRegexpf asserts that a specified regexp does not match a string. // -// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") +// require.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// require.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1691,7 +1757,7 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args .. // NotSame asserts that two pointers do not reference the same object. // -// assert.NotSame(t, ptr1, ptr2) +// require.NotSame(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1707,7 +1773,7 @@ func NotSame(t TestingT, expected interface{}, actual interface{}, msgAndArgs .. // NotSamef asserts that two pointers do not reference the same object. // -// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") +// require.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1721,12 +1787,15 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, t.FailNow() } -// NotSubset asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubset asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // -// assert.NotSubset(t, [1, 3, 4], [1, 2]) -// assert.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) +// require.NotSubset(t, [1, 3, 4], [1, 2]) +// require.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) +// require.NotSubset(t, [1, 3, 4], {1: "one", 2: "two"}) +// require.NotSubset(t, {"x": 1, "y": 2}, ["z"]) func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1737,12 +1806,15 @@ func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...i t.FailNow() } -// NotSubsetf asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // -// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted") -// assert.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") +// require.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted") +// require.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") +// require.NotSubsetf(t, [1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted") +// require.NotSubsetf(t, {"x": 1, "y": 2}, ["z"], "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1777,7 +1849,7 @@ func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) { // Panics asserts that the code inside the specified PanicTestFunc panics. // -// assert.Panics(t, func(){ GoCrazy() }) +// require.Panics(t, func(){ GoCrazy() }) func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1792,7 +1864,7 @@ func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) +// require.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) func PanicsWithError(t TestingT, errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1807,7 +1879,7 @@ func PanicsWithError(t TestingT, errString string, f assert.PanicTestFunc, msgAn // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// require.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithErrorf(t TestingT, errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1821,7 +1893,7 @@ func PanicsWithErrorf(t TestingT, errString string, f assert.PanicTestFunc, msg // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) +// require.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) func PanicsWithValue(t TestingT, expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1835,7 +1907,7 @@ func PanicsWithValue(t TestingT, expected interface{}, f assert.PanicTestFunc, m // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// require.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithValuef(t TestingT, expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1848,7 +1920,7 @@ func PanicsWithValuef(t TestingT, expected interface{}, f assert.PanicTestFunc, // Panicsf asserts that the code inside the specified PanicTestFunc panics. // -// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") +// require.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1861,8 +1933,8 @@ func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{} // Positive asserts that the specified element is positive // -// assert.Positive(t, 1) -// assert.Positive(t, 1.23) +// require.Positive(t, 1) +// require.Positive(t, 1.23) func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1875,8 +1947,8 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) { // Positivef asserts that the specified element is positive // -// assert.Positivef(t, 1, "error message %s", "formatted") -// assert.Positivef(t, 1.23, "error message %s", "formatted") +// require.Positivef(t, 1, "error message %s", "formatted") +// require.Positivef(t, 1.23, "error message %s", "formatted") func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1889,8 +1961,8 @@ func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) { // Regexp asserts that a specified regexp matches a string. // -// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") -// assert.Regexp(t, "start...$", "it's not starting") +// require.Regexp(t, regexp.MustCompile("start"), "it's starting") +// require.Regexp(t, "start...$", "it's not starting") func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1903,8 +1975,8 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface // Regexpf asserts that a specified regexp matches a string. // -// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") +// require.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// require.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1917,7 +1989,7 @@ func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...in // Same asserts that two pointers reference the same object. // -// assert.Same(t, ptr1, ptr2) +// require.Same(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1933,7 +2005,7 @@ func Same(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...in // Samef asserts that two pointers reference the same object. // -// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") +// require.Samef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1947,11 +2019,15 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg t.FailNow() } -// Subset asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subset asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // -// assert.Subset(t, [1, 2, 3], [1, 2]) -// assert.Subset(t, {"x": 1, "y": 2}, {"x": 1}) +// require.Subset(t, [1, 2, 3], [1, 2]) +// require.Subset(t, {"x": 1, "y": 2}, {"x": 1}) +// require.Subset(t, [1, 2, 3], {1: "one", 2: "two"}) +// require.Subset(t, {"x": 1, "y": 2}, ["x"]) func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1962,11 +2038,15 @@ func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...inte t.FailNow() } -// Subsetf asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subsetf asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // -// assert.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted") -// assert.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") +// require.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted") +// require.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") +// require.Subsetf(t, [1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted") +// require.Subsetf(t, {"x": 1, "y": 2}, ["x"], "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1979,7 +2059,7 @@ func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args // True asserts that the specified value is true. // -// assert.True(t, myBool) +// require.True(t, myBool) func True(t TestingT, value bool, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1992,7 +2072,7 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) { // Truef asserts that the specified value is true. // -// assert.Truef(t, myBool, "error message %s", "formatted") +// require.Truef(t, myBool, "error message %s", "formatted") func Truef(t TestingT, value bool, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -2005,7 +2085,7 @@ func Truef(t TestingT, value bool, msg string, args ...interface{}) { // WithinDuration asserts that the two times are within duration delta of each other. // -// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) +// require.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -2018,7 +2098,7 @@ func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time // WithinDurationf asserts that the two times are within duration delta of each other. // -// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +// require.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -2031,7 +2111,7 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim // WithinRange asserts that a time is within a time range (inclusive). // -// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +// require.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func WithinRange(t TestingT, actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -2044,7 +2124,7 @@ func WithinRange(t TestingT, actual time.Time, start time.Time, end time.Time, m // WithinRangef asserts that a time is within a time range (inclusive). // -// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +// require.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -2055,7 +2135,19 @@ func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, t.FailNow() } -// YAMLEq asserts that two YAML strings are equivalent. +// YAMLEq asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// require.YAMLEq(t, expected, actual) func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -2066,7 +2158,19 @@ func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{ t.FailNow() } -// YAMLEqf asserts that two YAML strings are equivalent. +// YAMLEqf asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// require.YAMLEqf(t, expected, actual, "error message %s", "formatted") func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/require/require.go.tmpl b/require/require.go.tmpl index 55e42ddeb..8b3283685 100644 --- a/require/require.go.tmpl +++ b/require/require.go.tmpl @@ -1,4 +1,4 @@ -{{.Comment}} +{{ replace .Comment "assert." "require."}} func {{.DocInfo.Name}}(t TestingT, {{.Params}}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return } diff --git a/require/require_forward.go b/require/require_forward.go index 3c15ca36b..38d985a55 100644 --- a/require/require_forward.go +++ b/require/require_forward.go @@ -93,10 +93,19 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st ElementsMatchf(a.t, listA, listB, msg, args...) } -// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Empty asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // a.Empty(obj) +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -104,10 +113,19 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { Empty(a.t, object, msgAndArgs...) } -// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Emptyf asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // a.Emptyf(obj, "error message %s", "formatted") +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -129,7 +147,7 @@ func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs Equal(a.t, expected, actual, msgAndArgs...) } -// EqualError asserts that a function returned an error (i.e. not `nil`) +// EqualError asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() @@ -141,7 +159,7 @@ func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ... EqualError(a.t, theError, errString, msgAndArgs...) } -// EqualErrorf asserts that a function returned an error (i.e. not `nil`) +// EqualErrorf asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() @@ -223,12 +241,10 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string Equalf(a.t, expected, actual, msg, args...) } -// Error asserts that a function returned an error (i.e. not `nil`). +// Error asserts that a function returned a non-nil error (ie. an error). // -// actualObj, err := SomeFunction() -// if a.Error(err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// a.Error(err) func (a *Assertions) Error(err error, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -254,8 +270,8 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args .. ErrorAsf(a.t, err, target, msg, args...) } -// ErrorContains asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContains asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() // a.ErrorContains(err, expectedErrorSubString) @@ -266,8 +282,8 @@ func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs . ErrorContains(a.t, theError, contains, msgAndArgs...) } -// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContainsf asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() // a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") @@ -296,12 +312,10 @@ func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...inter ErrorIsf(a.t, err, target, msg, args...) } -// Errorf asserts that a function returned an error (i.e. not `nil`). +// Errorf asserts that a function returned a non-nil error (ie. an error). // -// actualObj, err := SomeFunction() -// if a.Errorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// a.Errorf(err, "error message %s", "formatted") func (a *Assertions) Errorf(err error, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -869,7 +883,29 @@ func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...in IsNonIncreasingf(a.t, object, msg, args...) } +// IsNotType asserts that the specified objects are not of the same type. +// +// a.IsNotType(&NotMyStruct{}, &MyStruct{}) +func (a *Assertions) IsNotType(theType interface{}, object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsNotType(a.t, theType, object, msgAndArgs...) +} + +// IsNotTypef asserts that the specified objects are not of the same type. +// +// a.IsNotTypef(&NotMyStruct{}, &MyStruct{}, "error message %s", "formatted") +func (a *Assertions) IsNotTypef(theType interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsNotTypef(a.t, theType, object, msg, args...) +} + // IsType asserts that the specified objects are of the same type. +// +// a.IsType(&MyStruct{}, &MyStruct{}) func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -878,6 +914,8 @@ func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAnd } // IsTypef asserts that the specified objects are of the same type. +// +// a.IsTypef(&MyStruct{}, &MyStruct{}, "error message %s", "formatted") func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1059,7 +1097,7 @@ func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) NoDirExistsf(a.t, path, msg, args...) } -// NoError asserts that a function returned no error (i.e. `nil`). +// NoError asserts that a function returned a nil error (ie. no error). // // actualObj, err := SomeFunction() // if a.NoError(err) { @@ -1072,7 +1110,7 @@ func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) { NoError(a.t, err, msgAndArgs...) } -// NoErrorf asserts that a function returned no error (i.e. `nil`). +// NoErrorf asserts that a function returned a nil error (ie. no error). // // actualObj, err := SomeFunction() // if a.NoErrorf(err, "error message %s", "formatted") { @@ -1163,8 +1201,7 @@ func (a *Assertions) NotElementsMatchf(listA interface{}, listB interface{}, msg NotElementsMatchf(a.t, listA, listB, msg, args...) } -// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmpty asserts that the specified object is NOT [Empty]. // // if a.NotEmpty(obj) { // assert.Equal(t, "two", obj[1]) @@ -1176,8 +1213,7 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { NotEmpty(a.t, object, msgAndArgs...) } -// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmptyf asserts that the specified object is NOT [Empty]. // // if a.NotEmptyf(obj, "error message %s", "formatted") { // assert.Equal(t, "two", obj[1]) @@ -1235,6 +1271,24 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str NotEqualf(a.t, expected, actual, msg, args...) } +// NotErrorAs asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func (a *Assertions) NotErrorAs(err error, target interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotErrorAs(a.t, err, target, msgAndArgs...) +} + +// NotErrorAsf asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func (a *Assertions) NotErrorAsf(err error, target interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotErrorAsf(a.t, err, target, msg, args...) +} + // NotErrorIs asserts that none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) { @@ -1361,12 +1415,15 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri NotSamef(a.t, expected, actual, msg, args...) } -// NotSubset asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubset asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.NotSubset([1, 3, 4], [1, 2]) // a.NotSubset({"x": 1, "y": 2}, {"z": 3}) +// a.NotSubset([1, 3, 4], {1: "one", 2: "two"}) +// a.NotSubset({"x": 1, "y": 2}, ["z"]) func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1374,12 +1431,15 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs NotSubset(a.t, list, subset, msgAndArgs...) } -// NotSubsetf asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.NotSubsetf([1, 3, 4], [1, 2], "error message %s", "formatted") // a.NotSubsetf({"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") +// a.NotSubsetf([1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted") +// a.NotSubsetf({"x": 1, "y": 2}, ["z"], "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1539,11 +1599,15 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, Samef(a.t, expected, actual, msg, args...) } -// Subset asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subset asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.Subset([1, 2, 3], [1, 2]) // a.Subset({"x": 1, "y": 2}, {"x": 1}) +// a.Subset([1, 2, 3], {1: "one", 2: "two"}) +// a.Subset({"x": 1, "y": 2}, ["x"]) func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1551,11 +1615,15 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ... Subset(a.t, list, subset, msgAndArgs...) } -// Subsetf asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subsetf asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.Subsetf([1, 2, 3], [1, 2], "error message %s", "formatted") // a.Subsetf({"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") +// a.Subsetf([1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted") +// a.Subsetf({"x": 1, "y": 2}, ["x"], "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1623,7 +1691,19 @@ func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Ti WithinRangef(a.t, actual, start, end, msg, args...) } -// YAMLEq asserts that two YAML strings are equivalent. +// YAMLEq asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// a.YAMLEq(expected, actual) func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1631,7 +1711,19 @@ func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interf YAMLEq(a.t, expected, actual, msgAndArgs...) } -// YAMLEqf asserts that two YAML strings are equivalent. +// YAMLEqf asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// a.YAMLEqf(expected, actual, "error message %s", "formatted") func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/require/requirements_test.go b/require/requirements_test.go index 824b159fa..7cb63a554 100644 --- a/require/requirements_test.go +++ b/require/requirements_test.go @@ -29,6 +29,9 @@ type MockT struct { Failed bool } +// Helper is like [testing.T.Helper] but does nothing. +func (MockT) Helper() {} + func (t *MockT) FailNow() { t.Failed = true } @@ -38,6 +41,7 @@ func (t *MockT) Errorf(format string, args ...interface{}) { } func TestImplements(t *testing.T) { + t.Parallel() Implements(t, (*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject)) @@ -49,6 +53,7 @@ func TestImplements(t *testing.T) { } func TestIsType(t *testing.T) { + t.Parallel() IsType(t, new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) @@ -60,6 +65,7 @@ func TestIsType(t *testing.T) { } func TestEqual(t *testing.T) { + t.Parallel() Equal(t, 1, 1) @@ -72,6 +78,7 @@ func TestEqual(t *testing.T) { } func TestNotEqual(t *testing.T) { + t.Parallel() NotEqual(t, 1, 2) mockT := new(MockT) @@ -82,6 +89,7 @@ func TestNotEqual(t *testing.T) { } func TestExactly(t *testing.T) { + t.Parallel() a := float32(1) b := float32(1) @@ -97,6 +105,7 @@ func TestExactly(t *testing.T) { } func TestNotNil(t *testing.T) { + t.Parallel() NotNil(t, new(AssertionTesterConformingObject)) @@ -108,6 +117,7 @@ func TestNotNil(t *testing.T) { } func TestNil(t *testing.T) { + t.Parallel() Nil(t, nil) @@ -119,6 +129,7 @@ func TestNil(t *testing.T) { } func TestTrue(t *testing.T) { + t.Parallel() True(t, true) @@ -130,6 +141,7 @@ func TestTrue(t *testing.T) { } func TestFalse(t *testing.T) { + t.Parallel() False(t, false) @@ -141,6 +153,7 @@ func TestFalse(t *testing.T) { } func TestContains(t *testing.T) { + t.Parallel() Contains(t, "Hello World", "Hello") @@ -152,6 +165,7 @@ func TestContains(t *testing.T) { } func TestNotContains(t *testing.T) { + t.Parallel() NotContains(t, "Hello World", "Hello!") @@ -163,6 +177,7 @@ func TestNotContains(t *testing.T) { } func TestPanics(t *testing.T) { + t.Parallel() Panics(t, func() { panic("Panic!") @@ -176,6 +191,7 @@ func TestPanics(t *testing.T) { } func TestNotPanics(t *testing.T) { + t.Parallel() NotPanics(t, func() {}) @@ -189,6 +205,7 @@ func TestNotPanics(t *testing.T) { } func TestNoError(t *testing.T) { + t.Parallel() NoError(t, nil) @@ -200,6 +217,7 @@ func TestNoError(t *testing.T) { } func TestError(t *testing.T) { + t.Parallel() Error(t, errors.New("some error")) @@ -211,6 +229,7 @@ func TestError(t *testing.T) { } func TestErrorContains(t *testing.T) { + t.Parallel() ErrorContains(t, errors.New("some error: another error"), "some error") @@ -222,6 +241,7 @@ func TestErrorContains(t *testing.T) { } func TestEqualError(t *testing.T) { + t.Parallel() EqualError(t, errors.New("some error"), "some error") @@ -233,6 +253,7 @@ func TestEqualError(t *testing.T) { } func TestEmpty(t *testing.T) { + t.Parallel() Empty(t, "") @@ -244,6 +265,7 @@ func TestEmpty(t *testing.T) { } func TestNotEmpty(t *testing.T) { + t.Parallel() NotEmpty(t, "x") @@ -255,6 +277,7 @@ func TestNotEmpty(t *testing.T) { } func TestWithinDuration(t *testing.T) { + t.Parallel() a := time.Now() b := a.Add(10 * time.Second) @@ -269,6 +292,7 @@ func TestWithinDuration(t *testing.T) { } func TestInDelta(t *testing.T) { + t.Parallel() InDelta(t, 1.001, 1, 0.01) @@ -280,6 +304,7 @@ func TestInDelta(t *testing.T) { } func TestZero(t *testing.T) { + t.Parallel() Zero(t, "") @@ -291,6 +316,7 @@ func TestZero(t *testing.T) { } func TestNotZero(t *testing.T) { + t.Parallel() NotZero(t, "x") @@ -302,6 +328,8 @@ func TestNotZero(t *testing.T) { } func TestJSONEq_EqualSONString(t *testing.T) { + t.Parallel() + mockT := new(MockT) JSONEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) if mockT.Failed { @@ -310,6 +338,8 @@ func TestJSONEq_EqualSONString(t *testing.T) { } func TestJSONEq_EquivalentButNotEqual(t *testing.T) { + t.Parallel() + mockT := new(MockT) JSONEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) if mockT.Failed { @@ -318,6 +348,8 @@ func TestJSONEq_EquivalentButNotEqual(t *testing.T) { } func TestJSONEq_HashOfArraysAndHashes(t *testing.T) { + t.Parallel() + mockT := new(MockT) JSONEq(mockT, "{\r\n\t\"numeric\": 1.5,\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]],\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\"\r\n}", "{\r\n\t\"numeric\": 1.5,\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\",\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]]\r\n}") @@ -327,6 +359,8 @@ func TestJSONEq_HashOfArraysAndHashes(t *testing.T) { } func TestJSONEq_Array(t *testing.T) { + t.Parallel() + mockT := new(MockT) JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) if mockT.Failed { @@ -335,6 +369,8 @@ func TestJSONEq_Array(t *testing.T) { } func TestJSONEq_HashAndArrayNotEquivalent(t *testing.T) { + t.Parallel() + mockT := new(MockT) JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) if !mockT.Failed { @@ -343,6 +379,8 @@ func TestJSONEq_HashAndArrayNotEquivalent(t *testing.T) { } func TestJSONEq_HashesNotEquivalent(t *testing.T) { + t.Parallel() + mockT := new(MockT) JSONEq(mockT, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) if !mockT.Failed { @@ -351,6 +389,8 @@ func TestJSONEq_HashesNotEquivalent(t *testing.T) { } func TestJSONEq_ActualIsNotJSON(t *testing.T) { + t.Parallel() + mockT := new(MockT) JSONEq(mockT, `{"foo": "bar"}`, "Not JSON") if !mockT.Failed { @@ -359,6 +399,8 @@ func TestJSONEq_ActualIsNotJSON(t *testing.T) { } func TestJSONEq_ExpectedIsNotJSON(t *testing.T) { + t.Parallel() + mockT := new(MockT) JSONEq(mockT, "Not JSON", `{"foo": "bar", "hello": "world"}`) if !mockT.Failed { @@ -367,6 +409,8 @@ func TestJSONEq_ExpectedIsNotJSON(t *testing.T) { } func TestJSONEq_ExpectedAndActualNotJSON(t *testing.T) { + t.Parallel() + mockT := new(MockT) JSONEq(mockT, "Not JSON", "Not JSON") if !mockT.Failed { @@ -375,6 +419,8 @@ func TestJSONEq_ExpectedAndActualNotJSON(t *testing.T) { } func TestJSONEq_ArraysOfDifferentOrder(t *testing.T) { + t.Parallel() + mockT := new(MockT) JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) if !mockT.Failed { @@ -383,6 +429,8 @@ func TestJSONEq_ArraysOfDifferentOrder(t *testing.T) { } func TestYAMLEq_EqualYAMLString(t *testing.T) { + t.Parallel() + mockT := new(MockT) YAMLEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) if mockT.Failed { @@ -391,6 +439,8 @@ func TestYAMLEq_EqualYAMLString(t *testing.T) { } func TestYAMLEq_EquivalentButNotEqual(t *testing.T) { + t.Parallel() + mockT := new(MockT) YAMLEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) if mockT.Failed { @@ -399,6 +449,8 @@ func TestYAMLEq_EquivalentButNotEqual(t *testing.T) { } func TestYAMLEq_HashOfArraysAndHashes(t *testing.T) { + t.Parallel() + mockT := new(MockT) expected := ` numeric: 1.5 @@ -432,6 +484,8 @@ array: } func TestYAMLEq_Array(t *testing.T) { + t.Parallel() + mockT := new(MockT) YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) if mockT.Failed { @@ -440,6 +494,8 @@ func TestYAMLEq_Array(t *testing.T) { } func TestYAMLEq_HashAndArrayNotEquivalent(t *testing.T) { + t.Parallel() + mockT := new(MockT) YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) if !mockT.Failed { @@ -448,6 +504,8 @@ func TestYAMLEq_HashAndArrayNotEquivalent(t *testing.T) { } func TestYAMLEq_HashesNotEquivalent(t *testing.T) { + t.Parallel() + mockT := new(MockT) YAMLEq(mockT, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) if !mockT.Failed { @@ -456,6 +514,8 @@ func TestYAMLEq_HashesNotEquivalent(t *testing.T) { } func TestYAMLEq_ActualIsSimpleString(t *testing.T) { + t.Parallel() + mockT := new(MockT) YAMLEq(mockT, `{"foo": "bar"}`, "Simple String") if !mockT.Failed { @@ -464,6 +524,8 @@ func TestYAMLEq_ActualIsSimpleString(t *testing.T) { } func TestYAMLEq_ExpectedIsSimpleString(t *testing.T) { + t.Parallel() + mockT := new(MockT) YAMLEq(mockT, "Simple String", `{"foo": "bar", "hello": "world"}`) if !mockT.Failed { @@ -472,6 +534,8 @@ func TestYAMLEq_ExpectedIsSimpleString(t *testing.T) { } func TestYAMLEq_ExpectedAndActualSimpleString(t *testing.T) { + t.Parallel() + mockT := new(MockT) YAMLEq(mockT, "Simple String", "Simple String") if mockT.Failed { @@ -480,6 +544,8 @@ func TestYAMLEq_ExpectedAndActualSimpleString(t *testing.T) { } func TestYAMLEq_ArraysOfDifferentOrder(t *testing.T) { + t.Parallel() + mockT := new(MockT) YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) if !mockT.Failed { @@ -518,6 +584,8 @@ func ExampleComparisonAssertionFunc() { } func TestComparisonAssertionFunc(t *testing.T) { + t.Parallel() + type iface interface { Name() string } @@ -579,6 +647,8 @@ func ExampleValueAssertionFunc() { } func TestValueAssertionFunc(t *testing.T) { + t.Parallel() + tests := []struct { name string value interface{} @@ -625,6 +695,8 @@ func ExampleBoolAssertionFunc() { } func TestBoolAssertionFunc(t *testing.T) { + t.Parallel() + tests := []struct { name string value bool @@ -668,6 +740,8 @@ func ExampleErrorAssertionFunc() { } func TestErrorAssertionFunc(t *testing.T) { + t.Parallel() + tests := []struct { name string err error @@ -685,6 +759,8 @@ func TestErrorAssertionFunc(t *testing.T) { } func TestEventuallyWithTFalse(t *testing.T) { + t.Parallel() + mockT := new(MockT) condition := func(collect *assert.CollectT) { @@ -696,6 +772,8 @@ func TestEventuallyWithTFalse(t *testing.T) { } func TestEventuallyWithTTrue(t *testing.T) { + t.Parallel() + mockT := new(MockT) counter := 0 diff --git a/suite/doc.go b/suite/doc.go index 8d55a3aa8..05a562f72 100644 --- a/suite/doc.go +++ b/suite/doc.go @@ -5,6 +5,8 @@ // or individual tests (depending on which interface(s) you // implement). // +// The suite package does not support parallel tests. See [issue 934]. +// // A testing suite is usually built by first extending the built-in // suite functionality from suite.Suite in testify. Alternatively, // you could reproduce that logic on your own if you wanted (you @@ -63,4 +65,6 @@ // func TestExampleTestSuite(t *testing.T) { // suite.Run(t, new(ExampleTestSuite)) // } +// +// [issue 934]: https://github.com/stretchr/testify/issues/934 package suite diff --git a/suite/stats.go b/suite/stats.go index 261da37f7..be4ccd679 100644 --- a/suite/stats.go +++ b/suite/stats.go @@ -16,26 +16,30 @@ type TestInformation struct { } func newSuiteInformation() *SuiteInformation { - testStats := make(map[string]*TestInformation) - return &SuiteInformation{ - TestStats: testStats, + TestStats: make(map[string]*TestInformation), } } -func (s SuiteInformation) start(testName string) { +func (s *SuiteInformation) start(testName string) { + if s == nil { + return + } s.TestStats[testName] = &TestInformation{ TestName: testName, Start: time.Now(), } } -func (s SuiteInformation) end(testName string, passed bool) { +func (s *SuiteInformation) end(testName string, passed bool) { + if s == nil { + return + } s.TestStats[testName].End = time.Now() s.TestStats[testName].Passed = passed } -func (s SuiteInformation) Passed() bool { +func (s *SuiteInformation) Passed() bool { for _, stats := range s.TestStats { if !stats.Passed { return false diff --git a/suite/stats_test.go b/suite/stats_test.go index 4446a6d64..d68db4e56 100644 --- a/suite/stats_test.go +++ b/suite/stats_test.go @@ -27,3 +27,14 @@ func TestPassedReturnsFalseWhenSomeTestFails(t *testing.T) { assert.False(t, sinfo.Passed()) } + +func TestPassedReturnsFalseWhenAllTestsFail(t *testing.T) { + sinfo := newSuiteInformation() + sinfo.TestStats = map[string]*TestInformation{ + "Test1": {TestName: "Test1", Passed: false}, + "Test2": {TestName: "Test2", Passed: false}, + "Test3": {TestName: "Test3", Passed: false}, + } + + assert.False(t, sinfo.Passed()) +} diff --git a/suite/suite.go b/suite/suite.go index f768f3ab7..350f3a741 100644 --- a/suite/suite.go +++ b/suite/suite.go @@ -19,7 +19,6 @@ import ( "github.com/stretchr/testify/require" ) -var allTestsFilter = func(_, _ string) (bool, error) { return true, nil } var matchMethod = flag.String("testify.m", "", "regular expression to select tests of the testify suite to run") // Suite is a basic testing suite with methods for storing and @@ -67,11 +66,15 @@ func (suite *Suite) Require() *require.Assertions { return suite.require } -// Assert returns an assert context for suite. Normally, you can call -// `suite.NoError(expected, actual)`, but for situations where the embedded -// methods are overridden (for example, you might want to override -// assert.Assertions with require.Assertions), this method is provided so you -// can call `suite.Assert().NoError()`. +// Assert returns an assert context for suite. Normally, you can call: +// +// suite.NoError(err) +// +// But for situations where the embedded methods are overridden (for example, +// you might want to override assert.Assertions with require.Assertions), this +// method is provided so you can call: +// +// suite.Assert().NoError(err) func (suite *Suite) Assert() *assert.Assertions { suite.mu.Lock() defer suite.mu.Unlock() @@ -120,6 +123,11 @@ func (suite *Suite) Run(name string, subtest func()) bool { }) } +type test = struct { + name string + run func(t *testing.T) +} + // Run takes a testing suite and runs all of the tests attached // to it. func Run(t *testing.T, suite TestingSuite) { @@ -137,45 +145,51 @@ func Run(t *testing.T, suite TestingSuite) { suite.SetT(t) suite.SetS(suite) - var suiteSetupDone bool - var stats *SuiteInformation if _, ok := suite.(WithStats); ok { stats = newSuiteInformation() } - tests := []testing.InternalTest{} + var tests []test methodFinder := reflect.TypeOf(suite) suiteName := methodFinder.Elem().Name() - for i := 0; i < methodFinder.NumMethod(); i++ { - method := methodFinder.Method(i) - - ok, err := methodFilter(method.Name) + var matchMethodRE *regexp.Regexp + if *matchMethod != "" { + var err error + matchMethodRE, err = regexp.Compile(*matchMethod) if err != nil { fmt.Fprintf(os.Stderr, "testify: invalid regexp for -m: %s\n", err) os.Exit(1) } + } + + for i := 0; i < methodFinder.NumMethod(); i++ { + method := methodFinder.Method(i) - if !ok { + if !strings.HasPrefix(method.Name, "Test") { continue } - - if !suiteSetupDone { - if stats != nil { - stats.Start = time.Now() - } - - if setupAllSuite, ok := suite.(SetupAllSuite); ok { - setupAllSuite.SetupSuite() - } - - suiteSetupDone = true + // Apply -testify.m filter + if matchMethodRE != nil && !matchMethodRE.MatchString(method.Name) { + continue } - - test := testing.InternalTest{ - Name: method.Name, - F: func(t *testing.T) { + // Check method signature + if method.Type.NumIn() > 1 || method.Type.NumOut() > 0 { + tests = append(tests, test{ + name: method.Name, + run: func(t *testing.T) { + t.Errorf( + "testify: suite method %q has invalid signature: expected no input or output parameters, method has %d input parameters and %d output parameters", + method.Name, method.Type.NumIn()-1, method.Type.NumOut(), + ) + }, + }) + continue + } + test := test{ + name: method.Name, + run: func(t *testing.T) { parentT := suite.T() suite.SetT(t) defer recoverAndFailOnPanic(t) @@ -184,10 +198,7 @@ func Run(t *testing.T, suite TestingSuite) { r := recover() - if stats != nil { - passed := !t.Failed() && r == nil - stats.end(method.Name, passed) - } + stats.end(method.Name, !t.Failed() && r == nil) if afterTestSuite, ok := suite.(AfterTest); ok { afterTestSuite.AfterTest(suiteName, method.Name) @@ -208,28 +219,37 @@ func Run(t *testing.T, suite TestingSuite) { beforeTestSuite.BeforeTest(methodFinder.Elem().Name(), method.Name) } - if stats != nil { - stats.start(method.Name) - } + stats.start(method.Name) method.Func.Call([]reflect.Value{reflect.ValueOf(suite)}) }, } tests = append(tests, test) } - if suiteSetupDone { - defer func() { - if tearDownAllSuite, ok := suite.(TearDownAllSuite); ok { - tearDownAllSuite.TearDownSuite() - } - - if suiteWithStats, measureStats := suite.(WithStats); measureStats { - stats.End = time.Now() - suiteWithStats.HandleStats(suiteName, stats) - } - }() + + if len(tests) == 0 { + return + } + + if stats != nil { + stats.Start = time.Now() + } + + if setupAllSuite, ok := suite.(SetupAllSuite); ok { + setupAllSuite.SetupSuite() } + defer func() { + if tearDownAllSuite, ok := suite.(TearDownAllSuite); ok { + tearDownAllSuite.TearDownSuite() + } + + if suiteWithStats, measureStats := suite.(WithStats); measureStats { + stats.End = time.Now() + suiteWithStats.HandleStats(suiteName, stats) + } + }() + // Tests will be shuffled if service name is in servicesToShuffle map if _, ok := servicesToShuffle[serviceName]; ok { rand.Shuffle(len(tests), func(i, j int) { @@ -240,38 +260,17 @@ func Run(t *testing.T, suite TestingSuite) { runTests(t, tests) } -// Filtering method according to set regular expression -// specified command-line argument -m -func methodFilter(name string) (bool, error) { - if ok, _ := regexp.MatchString("^Test", name); !ok { - return false, nil - } - return regexp.MatchString(*matchMethod, name) -} - -func runTests(t testing.TB, tests []testing.InternalTest) { +func runTests(t *testing.T, tests []test) { if len(tests) == 0 { t.Log("warning: no tests to run") return } - r, ok := t.(runner) - if !ok { // backwards compatibility with Go 1.6 and below - if !testing.RunTests(allTestsFilter, tests) { - t.Fail() - } - return - } - for _, test := range tests { - r.Run(test.Name, test.F) + t.Run(test.name, test.run) } } -type runner interface { - Run(name string, f func(t *testing.T)) bool -} - func cleanPath(path string) string { replaced := strings.Replace(path, monorepoPath+"/", "", -1) diff --git a/suite/suite_test.go b/suite/suite_test.go index db57a5fd2..1c193aaf2 100644 --- a/suite/suite_test.go +++ b/suite/suite_test.go @@ -4,7 +4,7 @@ import ( "bytes" "errors" "flag" - "io/ioutil" + "io" "math/rand" "os" "os/exec" @@ -16,6 +16,11 @@ import ( "github.com/stretchr/testify/require" ) +// allTestsFilter is a yes filter for testing.RunTests +func allTestsFilter(pat, str string) (bool, error) { + return true, nil +} + // SuiteRequireTwice is intended to test the usage of suite.Require in two // different tests type SuiteRequireTwice struct{ Suite } @@ -440,7 +445,7 @@ func (sc *StdoutCapture) StopCapture() (string, error) { } os.Stdout.Close() os.Stdout = sc.oldStdout - bytes, err := ioutil.ReadAll(sc.readPipe) + bytes, err := io.ReadAll(sc.readPipe) if err != nil { return "", err } @@ -604,14 +609,44 @@ func TestFailfastSuite(t *testing.T) { }}, ) assert.False(t, ok) + var expect []string if failFast { // Test A Fails and because we are running with failfast Test B never runs and we proceed straight to TearDownSuite - assert.Equal(t, "SetupSuite;SetupTest;Test A Fails;TearDownTest;TearDownSuite", strings.Join(s.callOrder, ";")) + expect = []string{"SetupSuite", "SetupTest", "Test A Fails", "TearDownTest", "TearDownSuite"} } else { // Test A Fails and because we are running without failfast we continue and run Test B and then proceed to TearDownSuite - assert.Equal(t, "SetupSuite;SetupTest;Test A Fails;TearDownTest;SetupTest;Test B Passes;TearDownTest;TearDownSuite", strings.Join(s.callOrder, ";")) + expect = []string{"SetupSuite", "SetupTest", "Test A Fails", "TearDownTest", "SetupTest", "Test B Passes", "TearDownTest", "TearDownSuite"} + } + callOrderAssert(t, expect, s.callOrder) +} + +type tHelper interface { + Helper() +} + +// callOrderAssert is a help with confirms that asserts that expect +// matches one or more times in callOrder. This makes it compatible +// with go test flag -count=X where X > 1. +func callOrderAssert(t *testing.T, expect, callOrder []string) { + var ti interface{} = t + if h, ok := ti.(tHelper); ok { + h.Helper() } + + callCount := len(callOrder) + expectCount := len(expect) + if callCount > expectCount && callCount%expectCount == 0 { + // Command line flag -count=X where X > 1. + for len(callOrder) >= expectCount { + assert.Equal(t, expect, callOrder[:expectCount]) + callOrder = callOrder[expectCount:] + } + return + } + + assert.Equal(t, expect, callOrder) } + func TestFailfastSuiteFailFastOn(t *testing.T) { // To test this with failfast on (and isolated from other intended test failures in our test suite) we launch it in its own process cmd := exec.Command("go", "test", "-v", "-race", "-run", "TestFailfastSuite", "-failfast") @@ -716,3 +751,65 @@ func TestUnInitializedSuites(t *testing.T) { }) }) } + +// SuiteSignatureValidationTester tests valid and invalid method signatures. +type SuiteSignatureValidationTester struct { + Suite + + executedTestCount int + setUp bool + toreDown bool +} + +// SetupSuite runs once before any tests. +func (s *SuiteSignatureValidationTester) SetupSuite() { + s.setUp = true +} + +// TearDownSuite runs once after all tests. +func (s *SuiteSignatureValidationTester) TearDownSuite() { + s.toreDown = true +} + +// Valid test method — should run. +func (s *SuiteSignatureValidationTester) TestValidSignature() { + s.executedTestCount++ +} + +// Invalid: has return value. +func (s *SuiteSignatureValidationTester) TestInvalidSignatureReturnValue() interface{} { + s.executedTestCount++ + return nil +} + +// Invalid: has input arg. +func (s *SuiteSignatureValidationTester) TestInvalidSignatureArg(somearg string) { + s.executedTestCount++ +} + +// Invalid: both input arg and return value. +func (s *SuiteSignatureValidationTester) TestInvalidSignatureBoth(somearg string) interface{} { + s.executedTestCount++ + return nil +} + +// TestSuiteSignatureValidation ensures that invalid signature methods fail and valid method runs. +func TestSuiteSignatureValidation(t *testing.T) { + suiteTester := new(SuiteSignatureValidationTester) + + ok := testing.RunTests(allTestsFilter, []testing.InternalTest{ + { + Name: "signature validation", + F: func(t *testing.T) { + Run(t, suiteTester) + }, + }, + }) + + require.False(t, ok, "Suite should fail due to invalid method signatures") + + assert.Equal(t, 1, suiteTester.executedTestCount, "Only the valid test method should have been executed") + + assert.True(t, suiteTester.setUp, "SetupSuite should have been executed") + assert.True(t, suiteTester.toreDown, "TearDownSuite should have been executed") +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 9d3051eab..93d537253 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,9 +1,6 @@ # github.com/davecgh/go-spew v1.1.1 ## explicit github.com/davecgh/go-spew/spew -# github.com/pmezard/go-difflib v1.0.0 -## explicit -github.com/pmezard/go-difflib/difflib # github.com/stretchr/objx v0.5.2 ## explicit; go 1.20 github.com/stretchr/objx