mirror of https://github.com/stretchr/testify.git
Compare commits
38 Commits
Author | SHA1 | Date |
---|---|---|
|
5ac6528bff | |
|
d0e0f4961b | |
|
b561f16e87 | |
|
a948a8c402 | |
|
c3915e850a | |
|
16020e8cbc | |
|
75df9d50d4 | |
|
33be8f984a | |
|
a9e8aed155 | |
|
3b8bd9bf7d | |
|
1e7fb5865a | |
|
c6ac9bb91d | |
|
65f73866c0 | |
|
b1c9368f81 | |
|
5a5ac85551 | |
|
53e0c918d4 | |
|
89086b0757 | |
|
098128fd10 | |
|
f784abc221 | |
|
dfda68b86f | |
|
3cf0926564 | |
|
c60c3bd7fb | |
|
1c717c00c1 | |
|
ccb5e7f656 | |
|
ca6698b8a1 | |
|
7c367bb7bc | |
|
e6a990c21d | |
|
cfee2346d7 | |
|
f8c628e5a1 | |
|
014ae9a7a4 | |
|
30f3cef5ad | |
|
d57bac8721 | |
|
7f10816c93 | |
|
176474a4c9 | |
|
28e0be5092 | |
|
4a90eff4ae | |
|
3ca01f4bc3 | |
|
0e5b59666a |
|
@ -0,0 +1,12 @@
|
|||
#!/bin/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
|
|
@ -15,8 +15,10 @@ jobs:
|
|||
uses: actions/setup-go@v5
|
||||
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:
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
|
@ -6,5 +6,12 @@ pull requests.
|
|||
* @boyan-soubachov
|
||||
* @dolmen
|
||||
* @MovieStoreGuy
|
||||
* @arjunmahishi
|
||||
* @brackendawson
|
||||
|
||||
## Approvers
|
||||
|
||||
The individuals listed below are active in the project and have the ability to approve pull
|
||||
requests.
|
||||
|
||||
* @arjunmahishi
|
||||
* @ccoVeille
|
||||
|
|
187
README.md
187
README.md
|
@ -20,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
|
||||
|
@ -38,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 inequality
|
||||
assert.NotEqual(t, 123, 456, "they should not be equal")
|
||||
|
||||
// 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)
|
||||
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -74,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)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -120,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"
|
||||
)
|
||||
|
||||
/*
|
||||
|
@ -130,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
|
||||
|
@ -142,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)
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -155,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
|
||||
|
@ -177,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)
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -235,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))
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -276,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))
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -329,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!")
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ import (
|
|||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
|
@ -101,7 +100,7 @@ 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
|
||||
}
|
||||
|
@ -183,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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -24,6 +24,8 @@ import (
|
|||
"github.com/stretchr/testify/assert/yaml"
|
||||
)
|
||||
|
||||
const stackFrameBufferSize = 10
|
||||
|
||||
//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl"
|
||||
|
||||
// TestingT is an interface wrapper around *testing.T
|
||||
|
@ -212,57 +214,73 @@ the problem actually occurred in calling code.*/
|
|||
func CallerInfo() []string {
|
||||
|
||||
var pc uintptr
|
||||
var ok bool
|
||||
var file string
|
||||
var line int
|
||||
var name string
|
||||
|
||||
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.
|
||||
pcs := make([]uintptr, stackFrameBufferSize)
|
||||
offset := 1
|
||||
|
||||
for {
|
||||
n := runtime.Callers(offset, pcs)
|
||||
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// This is a huge edge case, but it will panic if this is the case, see #180
|
||||
if file == "<autogenerated>" {
|
||||
break
|
||||
}
|
||||
frames := runtime.CallersFrames(pcs[:n])
|
||||
|
||||
f := runtime.FuncForPC(pc)
|
||||
if f == nil {
|
||||
break
|
||||
}
|
||||
name = f.Name()
|
||||
for {
|
||||
frame, more := frames.Next()
|
||||
pc = frame.PC
|
||||
file = frame.File
|
||||
line = frame.Line
|
||||
|
||||
// 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
|
||||
}
|
||||
// This is a huge edge case, but it will panic if this is the case, see #180
|
||||
if file == "<autogenerated>" {
|
||||
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))
|
||||
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
|
||||
segments := strings.Split(name, ".")
|
||||
name = segments[len(segments)-1]
|
||||
if isTest(name, "Test") ||
|
||||
isTest(name, "Benchmark") ||
|
||||
isTest(name, "Example") {
|
||||
break
|
||||
}
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
}
|
||||
// We know we already have less than a buffer's worth of frames
|
||||
offset += stackFrameBufferSize
|
||||
|
||||
// Drop the package
|
||||
segments := strings.Split(name, ".")
|
||||
name = segments[len(segments)-1]
|
||||
if isTest(name, "Test") ||
|
||||
isTest(name, "Benchmark") ||
|
||||
isTest(name, "Example") {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return callers
|
||||
|
@ -444,7 +462,7 @@ func IsType(t TestingT, expectedType interface{}, object interface{}, 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...)
|
||||
return Fail(t, fmt.Sprintf("Object expected to be of type %T, but was %T", expectedType, object), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
|
@ -1069,7 +1087,7 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
|
|||
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
|
||||
|
@ -2102,7 +2120,7 @@ func ErrorIs(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 be in err chain:\n"+
|
||||
"expected: %q\n"+
|
||||
|
@ -2125,7 +2143,7 @@ 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"+
|
||||
|
@ -2143,11 +2161,11 @@ func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{
|
|||
return true
|
||||
}
|
||||
|
||||
chain := buildErrorChainString(err)
|
||||
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", reflect.ValueOf(target).Elem().Type(), chain,
|
||||
), msgAndArgs...)
|
||||
}
|
||||
|
||||
|
@ -2161,24 +2179,46 @@ func NotErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interfa
|
|||
return true
|
||||
}
|
||||
|
||||
chain := buildErrorChainString(err)
|
||||
chain := buildErrorChainString(err, true)
|
||||
|
||||
return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+
|
||||
"found: %q\n"+
|
||||
"in chain: %s", target, chain,
|
||||
"found: %s\n"+
|
||||
"in chain: %s", reflect.ValueOf(target).Elem().Type(), chain,
|
||||
), msgAndArgs...)
|
||||
}
|
||||
|
||||
func buildErrorChainString(err error) string {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -2118,7 +2118,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))
|
||||
|
@ -2151,11 +2151,11 @@ func TestZero(t *testing.T) {
|
|||
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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2163,11 +2163,11 @@ func TestNotZero(t *testing.T) {
|
|||
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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3175,11 +3175,13 @@ func parseLabeledOutput(output string) []labeledContent {
|
|||
}
|
||||
|
||||
type captureTestingT struct {
|
||||
msg string
|
||||
failed bool
|
||||
msg string
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -3188,6 +3190,10 @@ 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 {
|
||||
|
@ -3348,50 +3354,82 @@ func TestNotErrorIs(t *testing.T) {
|
|||
|
||||
func TestErrorAs(t *testing.T) {
|
||||
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: "" +
|
||||
"Should be in error chain:\n" +
|
||||
"expected: *assert.customError\n" +
|
||||
"in chain: \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(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)
|
||||
}
|
||||
if res == mockT.Failed() {
|
||||
t.Errorf("The test result (%t) should be reflected in the testing.T type (%t)", res, !mockT.Failed())
|
||||
}
|
||||
mockT.checkResultAndErrMsg(t, tt.result, res, tt.resultErrMsg)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotErrorAs(t *testing.T) {
|
||||
tests := []struct {
|
||||
err error
|
||||
result bool
|
||||
err error
|
||||
result bool
|
||||
resultErrMsg string
|
||||
}{
|
||||
{fmt.Errorf("wrap: %w", &customError{}), false},
|
||||
{io.EOF, true},
|
||||
{nil, true},
|
||||
{
|
||||
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(testing.T)
|
||||
mockT := new(captureTestingT)
|
||||
res := NotErrorAs(mockT, tt.err, &target)
|
||||
if res != tt.result {
|
||||
t.Errorf("NotErrorAs(%#v,%#v) should not return %t", tt.err, target, tt.result)
|
||||
}
|
||||
if res == mockT.Failed() {
|
||||
t.Errorf("The test result (%t) should be reflected in the testing.T type (%t)", res, !mockT.Failed())
|
||||
}
|
||||
mockT.checkResultAndErrMsg(t, tt.result, res, tt.resultErrMsg)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -546,7 +546,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))
|
||||
|
|
|
@ -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
|
||||
|
|
20
mock/mock.go
20
mock/mock.go
|
@ -208,9 +208,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.
|
||||
//
|
||||
// test.On("func", mock.Anything).Unset()
|
||||
// For example, the only handler remaining after the following would be "MyMethod(2, 2)":
|
||||
//
|
||||
// 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
|
||||
|
||||
|
@ -331,7 +338,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()
|
||||
|
@ -494,7 +504,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:
|
||||
|
@ -514,7 +524,7 @@ func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Argumen
|
|||
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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
//
|
||||
// The `require` package have same global functions as in the `assert` package,
|
||||
// but instead of returning a boolean result they call `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.
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"bytes"
|
||||
"errors"
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
@ -440,7 +440,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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue