├── .ci.gofmt.sh ├── .ci.gogenerate.sh ├── .ci.govet.sh ├── .ci.readme.fmt.sh ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── main.yml │ └── release.yml ├── .gitignore ├── .mdsf.json ├── CONTRIBUTING.md ├── EMERITUS.md ├── LICENSE ├── MAINTAINERS.md ├── README.md ├── _codegen ├── .gitignore ├── go.mod ├── go.sum └── main.go ├── assert ├── assertion_compare.go ├── assertion_compare_test.go ├── assertion_format.go ├── assertion_format.go.tmpl ├── assertion_forward.go ├── assertion_forward.go.tmpl ├── assertion_order.go ├── assertion_order_test.go ├── assertions.go ├── assertions_test.go ├── doc.go ├── errors.go ├── forward_assertions.go ├── forward_assertions_test.go ├── http_assertions.go ├── http_assertions_test.go ├── internal │ └── unsafetests │ │ ├── doc.go │ │ └── unsafetests_test.go └── yaml │ ├── yaml_custom.go │ ├── yaml_default.go │ └── yaml_fail.go ├── doc.go ├── go.mod ├── go.sum ├── http ├── doc.go ├── test_response_writer.go └── test_round_tripper.go ├── mock ├── doc.go ├── mock.go └── mock_test.go ├── package_test.go ├── require ├── doc.go ├── forward_requirements.go ├── forward_requirements_test.go ├── require.go ├── require.go.tmpl ├── require_forward.go ├── require_forward.go.tmpl ├── requirements.go └── requirements_test.go └── suite ├── doc.go ├── interfaces.go ├── stats.go ├── stats_test.go ├── suite.go └── suite_test.go /.ci.gofmt.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ -n "$(gofmt -l .)" ]; then 4 | echo "Go code is not formatted:" 5 | gofmt -d . 6 | exit 1 7 | fi 8 | 9 | go generate ./... 10 | if [ -n "$(git status -s -uno)" ]; then 11 | echo "Go generate output does not match commit." 12 | echo "Did you forget to run go generate ./... ?" 13 | exit 1 14 | fi 15 | -------------------------------------------------------------------------------- /.ci.gogenerate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # If GOMOD is defined we are running with Go Modules enabled, either 4 | # automatically or via the GO111MODULE=on environment variable. Codegen only 5 | # works with modules, so skip generation if modules is not in use. 6 | if [[ -z "$(go env GOMOD)" ]]; then 7 | echo "Skipping go generate because modules not enabled and required" 8 | exit 0 9 | fi 10 | 11 | go generate ./... 12 | if [ -n "$(git diff)" ]; then 13 | echo "Go generate had not been run" 14 | git diff 15 | exit 1 16 | fi 17 | -------------------------------------------------------------------------------- /.ci.govet.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | go vet ./... 6 | -------------------------------------------------------------------------------- /.ci.readme.fmt.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Verify that the code snippets in README.md are formatted. 4 | # The tool https://github.com/hougesen/mdsf is used. 5 | 6 | if [ -n "$(mdsf verify --config .mdsf.json --log-level error README.md 2>&1)" ]; then 7 | echo "Go code in the README.md is not formatted." 8 | echo "Did you forget to run 'mdsf format --config .mdsf.json README.md'?" 9 | mdsf format --config .mdsf.json README.md 10 | git diff 11 | exit 1 12 | fi 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Format to report a bug 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | 13 | ## Description 14 | 15 | 16 | ## Step To Reproduce 17 | 18 | 19 | ## Expected behavior 20 | 21 | 22 | ## Actual behavior 23 | 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Propose a new feature 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | 13 | ## Description 14 | 15 | 16 | ## Proposed solution 17 | 18 | 19 | ## Use case 20 | 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gomod 4 | directory: / 5 | schedule: 6 | interval: daily 7 | - package-ecosystem: github-actions 8 | directory: / 9 | schedule: 10 | interval: daily 11 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Summary 2 | 3 | 4 | ## Changes 5 | 6 | 7 | 8 | 9 | ## Motivation 10 | 11 | 12 | 13 | 14 | ## Related issues 15 | 16 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: All builds 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | strategy: 8 | matrix: 9 | go_version: 10 | - stable 11 | - oldstable 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Setup Go 15 | uses: actions/setup-go@v5 16 | with: 17 | go-version: ${{ matrix.go_version }} 18 | - run: npm install -g mdsf-cli 19 | - run: ./.ci.gogenerate.sh 20 | - run: ./.ci.gofmt.sh 21 | - run: ./.ci.readme.fmt.sh 22 | - run: ./.ci.govet.sh 23 | - run: go test -v -race ./... 24 | test: 25 | runs-on: ubuntu-latest 26 | strategy: 27 | matrix: 28 | go_version: 29 | - "1.17" 30 | - "1.18" 31 | - "1.19" 32 | - "1.20" 33 | - "1.21" 34 | - "1.22" 35 | steps: 36 | - uses: actions/checkout@v4 37 | - name: Setup Go 38 | uses: actions/setup-go@v5 39 | with: 40 | go-version: ${{ matrix.go_version }} 41 | - run: go test -v -race ./... 42 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Create release from new tag 2 | 3 | # this flow will be run only when new tags are pushed that match our pattern 4 | on: 5 | push: 6 | tags: 7 | - "v[0-9]+.[0-9]+.[0-9]+" 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: write 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | 18 | - name: Create GitHub release from tag 19 | uses: softprops/action-gh-release@v2 20 | with: 21 | generate_release_notes: true 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | 24 | .DS_Store 25 | 26 | # Output of "go test -c" 27 | /assert/assert.test 28 | /require/require.test 29 | /suite/suite.test 30 | /mock/mock.test 31 | -------------------------------------------------------------------------------- /.mdsf.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/hougesen/mdsf/main/schemas/v0.8.2/mdsf.schema.json", 3 | "format_finished_document": false, 4 | "languages": { 5 | "go": [ 6 | [ 7 | "gofmt", 8 | "goimports" 9 | ] 10 | ] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Testify 2 | 3 | So you'd like to contribute to Testify? First of all, thank you! Testify is widely used, so each 4 | contribution has a significant impact within the Golang community! Below you'll find everything you 5 | need to know to get up to speed on the project. 6 | 7 | ## Philosophy 8 | 9 | The Testify maintainers generally attempt to follow widely accepted practices within the Golang 10 | community. That being said, the first priority is always to make sure that the package is useful to 11 | the community. A few general guidelines are listed here: 12 | 13 | *Keep it simple (whenever practical)* - Try not to expand the API unless the new surface area 14 | provides meaningful benefits. For example, don't add functions because they might be useful to 15 | someone, someday. Add what is useful to specific users, today. 16 | 17 | *Ease of use is paramount* - This means good documentation and package organization. It also means 18 | that we should try hard to use meaningful, descriptive function names, avoid breaking the API 19 | unnecessarily, and try not to surprise the user. 20 | 21 | *Quality isn't an afterthought* - Testify is a testing library, so it seems reasonable that we 22 | should have a decent test suite. This is doubly important because a bug in Testify doesn't just mean 23 | a bug in our users' code, it means a bug in our users' tests, which means a potentially unnoticed 24 | and hard-to-find bug in our users' code. 25 | 26 | ## Pull Requests 27 | 28 | We welcome pull requests! Please include the following in the description: 29 | 30 | * Motivation, why your change is important or helpful 31 | * Example usage (if applicable) 32 | * Whether you intend to add / change behavior or fix a bug 33 | 34 | Please be aware that the maintainers may ask for changes. This isn't a commentary on the quality of 35 | your idea or your code. Testify is the result of many contributions from many individuals, so we 36 | need to enforce certain practices and patterns to keep the package easy for others to understand. 37 | Essentially, we recognize that there are often many good ways to do a given thing, but we have to 38 | pick one and stick with it. 39 | 40 | See `MAINTAINERS.md` for a list of users who can approve / merge your changes. 41 | 42 | ## Issues 43 | 44 | If you find a bug or think of a useful feature you'd like to see added to Testify, the best thing 45 | you can do is make the necessary changes and open a pull request (see above). If that isn't an 46 | option, or if you'd like to discuss your change before you write the code, open an issue! 47 | 48 | Please provide enough context in the issue description that other members of the community can 49 | easily understand what it is that you'd like to see. 50 | 51 | -------------------------------------------------------------------------------- /EMERITUS.md: -------------------------------------------------------------------------------- 1 | # Emeritus 2 | 3 | We would like to acknowledge previous testify maintainers and their huge contributions to our collective success: 4 | 5 | * @matryer 6 | * @glesica 7 | * @ernesto-jimenez 8 | * @mvdkleijn 9 | * @georgelesica-wf 10 | * @bencampbell-wf 11 | 12 | We thank these members for their service to this community. 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | # Testify Maintainers 2 | 3 | The individuals listed below are active in the project and have the ability to approve and merge 4 | pull requests. 5 | 6 | * @boyan-soubachov 7 | * @dolmen 8 | * @MovieStoreGuy 9 | * @brackendawson 10 | 11 | ## Approvers 12 | 13 | The individuals listed below are active in the project and have the ability to approve pull 14 | requests. 15 | 16 | * @arjunmahishi 17 | * @ccoVeille 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Testify - Thou Shalt Write Tests 2 | ================================ 3 | 4 | > [!NOTE] 5 | > Testify is being maintained at v1, no breaking changes will be accepted in this repo. 6 | > [See discussion about v2](https://github.com/stretchr/testify/discussions/1560). 7 | 8 | [![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) 9 | 10 | Go code (golang) set of packages that provide many tools for testifying that your code will behave as you intend. 11 | 12 | Features include: 13 | 14 | * [Easy assertions](#assert-package) 15 | * [Mocking](#mock-package) 16 | * [Testing suite interfaces and functions](#suite-package) 17 | 18 | Get started: 19 | 20 | * Install testify with [one line of code](#installation), or [update it with another](#staying-up-to-date) 21 | * For an introduction to writing test code in Go, see https://go.dev/doc/code#Testing 22 | * Check out the API Documentation https://pkg.go.dev/github.com/stretchr/testify 23 | * Use [testifylint](https://github.com/Antonboom/testifylint) (via [golangci-lint](https://golangci-lint.run/)) to avoid common mistakes 24 | * A little about [Test-Driven Development (TDD)](https://en.wikipedia.org/wiki/Test-driven_development) 25 | 26 | [`assert`](https://pkg.go.dev/github.com/stretchr/testify/assert "API documentation") package 27 | ------------------------------------------------------------------------------------------- 28 | 29 | The `assert` package provides some helpful methods that allow you to write better test code in Go. 30 | 31 | * Prints friendly, easy to read failure descriptions 32 | * Allows for very readable code 33 | * Optionally annotate each assertion with a message 34 | 35 | See it in action: 36 | 37 | ```go 38 | package yours 39 | 40 | import ( 41 | "testing" 42 | 43 | "github.com/stretchr/testify/assert" 44 | ) 45 | 46 | func TestSomething(t *testing.T) { 47 | // assert equality 48 | assert.Equal(t, 123, 123, "they should be equal") 49 | 50 | // assert inequality 51 | assert.NotEqual(t, 123, 456, "they should not be equal") 52 | 53 | // assert for nil (good for errors) 54 | assert.Nil(t, object) 55 | 56 | // assert for not nil (good when you expect something) 57 | if assert.NotNil(t, object) { 58 | // now we know that object isn't nil, we are safe to make 59 | // further assertions without causing any errors 60 | assert.Equal(t, "Something", object.Value) 61 | } 62 | } 63 | ``` 64 | 65 | * Every assert func takes the `testing.T` object as the first argument. This is how it writes the errors out through the normal `go test` capabilities. 66 | * Every assert func returns a bool indicating whether the assertion was successful or not, this is useful for if you want to go on making further assertions under certain conditions. 67 | 68 | if you assert many times, use the below: 69 | 70 | ```go 71 | package yours 72 | 73 | import ( 74 | "testing" 75 | 76 | "github.com/stretchr/testify/assert" 77 | ) 78 | 79 | func TestSomething(t *testing.T) { 80 | assert := assert.New(t) 81 | 82 | // assert equality 83 | assert.Equal(123, 123, "they should be equal") 84 | 85 | // assert inequality 86 | assert.NotEqual(123, 456, "they should not be equal") 87 | 88 | // assert for nil (good for errors) 89 | assert.Nil(object) 90 | 91 | // assert for not nil (good when you expect something) 92 | if assert.NotNil(object) { 93 | // now we know that object isn't nil, we are safe to make 94 | // further assertions without causing any errors 95 | assert.Equal("Something", object.Value) 96 | } 97 | } 98 | ``` 99 | 100 | [`require`](https://pkg.go.dev/github.com/stretchr/testify/require "API documentation") package 101 | --------------------------------------------------------------------------------------------- 102 | 103 | The `require` package provides same global functions as the `assert` package, but instead of returning a boolean result they terminate current test. 104 | These functions must be called from the goroutine running the test or benchmark function, not from other goroutines created during the test. 105 | Otherwise race conditions may occur. 106 | 107 | See [t.FailNow](https://pkg.go.dev/testing#T.FailNow) for details. 108 | 109 | [`mock`](https://pkg.go.dev/github.com/stretchr/testify/mock "API documentation") package 110 | ---------------------------------------------------------------------------------------- 111 | 112 | The `mock` package provides a mechanism for easily writing mock objects that can be used in place of real objects when writing test code. 113 | 114 | An example test function that tests a piece of code that relies on an external object `testObj`, can set up expectations (testify) and assert that they indeed happened: 115 | 116 | ```go 117 | package yours 118 | 119 | import ( 120 | "testing" 121 | 122 | "github.com/stretchr/testify/mock" 123 | ) 124 | 125 | /* 126 | Test objects 127 | */ 128 | 129 | // MyMockedObject is a mocked object that implements an interface 130 | // that describes an object that the code I am testing relies on. 131 | type MyMockedObject struct { 132 | mock.Mock 133 | } 134 | 135 | // DoSomething is a method on MyMockedObject that implements some interface 136 | // and just records the activity, and returns what the Mock object tells it to. 137 | // 138 | // In the real object, this method would do something useful, but since this 139 | // is a mocked object - we're just going to stub it out. 140 | // 141 | // NOTE: This method is not being tested here, code that uses this object is. 142 | func (m *MyMockedObject) DoSomething(number int) (bool, error) { 143 | args := m.Called(number) 144 | return args.Bool(0), args.Error(1) 145 | } 146 | 147 | /* 148 | Actual test functions 149 | */ 150 | 151 | // TestSomething is an example of how to use our test object to 152 | // make assertions about some target code we are testing. 153 | func TestSomething(t *testing.T) { 154 | // create an instance of our test object 155 | testObj := new(MyMockedObject) 156 | 157 | // set up expectations 158 | testObj.On("DoSomething", 123).Return(true, nil) 159 | 160 | // call the code we are testing 161 | targetFuncThatDoesSomethingWithObj(testObj) 162 | 163 | // assert that the expectations were met 164 | testObj.AssertExpectations(t) 165 | } 166 | 167 | // TestSomethingWithPlaceholder is a second example of how to use our test object to 168 | // make assertions about some target code we are testing. 169 | // This time using a placeholder. Placeholders might be used when the 170 | // data being passed in is normally dynamically generated and cannot be 171 | // predicted beforehand (eg. containing hashes that are time sensitive) 172 | func TestSomethingWithPlaceholder(t *testing.T) { 173 | // create an instance of our test object 174 | testObj := new(MyMockedObject) 175 | 176 | // set up expectations with a placeholder in the argument list 177 | testObj.On("DoSomething", mock.Anything).Return(true, nil) 178 | 179 | // call the code we are testing 180 | targetFuncThatDoesSomethingWithObj(testObj) 181 | 182 | // assert that the expectations were met 183 | testObj.AssertExpectations(t) 184 | 185 | } 186 | 187 | // TestSomethingElse2 is a third example that shows how you can use 188 | // the Unset method to cleanup handlers and then add new ones. 189 | func TestSomethingElse2(t *testing.T) { 190 | // create an instance of our test object 191 | testObj := new(MyMockedObject) 192 | 193 | // set up expectations with a placeholder in the argument list 194 | mockCall := testObj.On("DoSomething", mock.Anything).Return(true, nil) 195 | 196 | // call the code we are testing 197 | targetFuncThatDoesSomethingWithObj(testObj) 198 | 199 | // assert that the expectations were met 200 | testObj.AssertExpectations(t) 201 | 202 | // remove the handler now so we can add another one that takes precedence 203 | mockCall.Unset() 204 | 205 | // return false now instead of true 206 | testObj.On("DoSomething", mock.Anything).Return(false, nil) 207 | 208 | testObj.AssertExpectations(t) 209 | } 210 | ``` 211 | 212 | For more information on how to write mock code, check out the [API documentation for the `mock` package](https://pkg.go.dev/github.com/stretchr/testify/mock). 213 | 214 | You can use the [mockery tool](https://vektra.github.io/mockery/latest/) to autogenerate the mock code against an interface as well, making using mocks much quicker. 215 | 216 | [`suite`](https://pkg.go.dev/github.com/stretchr/testify/suite "API documentation") package 217 | ----------------------------------------------------------------------------------------- 218 | > [!WARNING] 219 | > The suite package does not support parallel tests. See [#934](https://github.com/stretchr/testify/issues/934). 220 | 221 | 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. 222 | 223 | An example suite is shown below: 224 | 225 | ```go 226 | // Basic imports 227 | import ( 228 | "testing" 229 | 230 | "github.com/stretchr/testify/assert" 231 | "github.com/stretchr/testify/suite" 232 | ) 233 | 234 | // Define the suite, and absorb the built-in basic suite 235 | // functionality from testify - including a T() method which 236 | // returns the current testing context 237 | type ExampleTestSuite struct { 238 | suite.Suite 239 | VariableThatShouldStartAtFive int 240 | } 241 | 242 | // Make sure that VariableThatShouldStartAtFive is set to five 243 | // before each test 244 | func (suite *ExampleTestSuite) SetupTest() { 245 | suite.VariableThatShouldStartAtFive = 5 246 | } 247 | 248 | // All methods that begin with "Test" are run as tests within a 249 | // suite. 250 | func (suite *ExampleTestSuite) TestExample() { 251 | assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive) 252 | } 253 | 254 | // In order for 'go test' to run this suite, we need to create 255 | // a normal test function and pass our suite to suite.Run 256 | func TestExampleTestSuite(t *testing.T) { 257 | suite.Run(t, new(ExampleTestSuite)) 258 | } 259 | ``` 260 | 261 | For a more complete example, using all of the functionality provided by the suite package, look at our [example testing suite](https://github.com/stretchr/testify/blob/master/suite/suite_test.go) 262 | 263 | For more information on writing suites, check out the [API documentation for the `suite` package](https://pkg.go.dev/github.com/stretchr/testify/suite). 264 | 265 | `Suite` object has assertion methods: 266 | 267 | ```go 268 | // Basic imports 269 | import ( 270 | "testing" 271 | 272 | "github.com/stretchr/testify/suite" 273 | ) 274 | 275 | // Define the suite, and absorb the built-in basic suite 276 | // functionality from testify - including assertion methods. 277 | type ExampleTestSuite struct { 278 | suite.Suite 279 | VariableThatShouldStartAtFive int 280 | } 281 | 282 | // Make sure that VariableThatShouldStartAtFive is set to five 283 | // before each test 284 | func (suite *ExampleTestSuite) SetupTest() { 285 | suite.VariableThatShouldStartAtFive = 5 286 | } 287 | 288 | // All methods that begin with "Test" are run as tests within a 289 | // suite. 290 | func (suite *ExampleTestSuite) TestExample() { 291 | suite.Equal(suite.VariableThatShouldStartAtFive, 5) 292 | } 293 | 294 | // In order for 'go test' to run this suite, we need to create 295 | // a normal test function and pass our suite to suite.Run 296 | func TestExampleTestSuite(t *testing.T) { 297 | suite.Run(t, new(ExampleTestSuite)) 298 | } 299 | ``` 300 | 301 | ------ 302 | 303 | Installation 304 | ============ 305 | 306 | To install Testify, use `go get`: 307 | 308 | go get github.com/stretchr/testify 309 | 310 | This will then make the following packages available to you: 311 | 312 | github.com/stretchr/testify/assert 313 | github.com/stretchr/testify/require 314 | github.com/stretchr/testify/mock 315 | github.com/stretchr/testify/suite 316 | github.com/stretchr/testify/http (deprecated) 317 | 318 | Import the `testify/assert` package into your code using this template: 319 | 320 | ```go 321 | package yours 322 | 323 | import ( 324 | "testing" 325 | 326 | "github.com/stretchr/testify/assert" 327 | ) 328 | 329 | func TestSomething(t *testing.T) { 330 | assert.True(t, true, "True is true!") 331 | } 332 | ``` 333 | 334 | ------ 335 | 336 | Staying up to date 337 | ================== 338 | 339 | To update Testify to the latest version, use `go get -u github.com/stretchr/testify`. 340 | 341 | ------ 342 | 343 | Supported go versions 344 | ================== 345 | 346 | We currently support the most recent major Go versions from 1.19 onward. 347 | 348 | ------ 349 | 350 | Contributing 351 | ============ 352 | 353 | Please feel free to submit issues, fork the repository and send pull requests! 354 | 355 | When submitting an issue, we ask that you please include a complete test function that demonstrates the issue. Extra credit for those using Testify to write the test code that demonstrates it. 356 | 357 | Code generation is used. [Look for `Code generated with`](https://github.com/search?q=repo%3Astretchr%2Ftestify%20%22Code%20generated%20with%22&type=code) at the top of some files. Run `go generate ./...` to update generated files. 358 | 359 | We also chat on the [Gophers Slack](https://gophers.slack.com) group in the `#testify` and `#testify-dev` channels. 360 | 361 | ------ 362 | 363 | License 364 | ======= 365 | 366 | This project is licensed under the terms of the MIT license. 367 | -------------------------------------------------------------------------------- /_codegen/.gitignore: -------------------------------------------------------------------------------- 1 | _codegen 2 | -------------------------------------------------------------------------------- /_codegen/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/stretchr/testify/_codegen 2 | 3 | go 1.11 4 | 5 | require github.com/ernesto-jimenez/gogen v0.0.0-20180125220232-d7d4131e6607 6 | -------------------------------------------------------------------------------- /_codegen/go.sum: -------------------------------------------------------------------------------- 1 | github.com/ernesto-jimenez/gogen v0.0.0-20180125220232-d7d4131e6607 h1:cTavhURetDkezJCvxFggiyLeP40Mrk/TtVg2+ycw1Es= 2 | github.com/ernesto-jimenez/gogen v0.0.0-20180125220232-d7d4131e6607/go.mod h1:Cg4fM0vhYWOZdgM7RIOSTRNIc8/VT7CXClC3Ni86lu4= 3 | -------------------------------------------------------------------------------- /_codegen/main.go: -------------------------------------------------------------------------------- 1 | // This program reads all assertion functions from the assert package and 2 | // automatically generates the corresponding requires and forwarded assertions 3 | 4 | package main 5 | 6 | import ( 7 | "bytes" 8 | "flag" 9 | "fmt" 10 | "go/ast" 11 | "go/build" 12 | "go/doc" 13 | "go/format" 14 | "go/importer" 15 | "go/parser" 16 | "go/token" 17 | "go/types" 18 | "io" 19 | "log" 20 | "os" 21 | "path" 22 | "regexp" 23 | "strings" 24 | "text/template" 25 | 26 | "github.com/ernesto-jimenez/gogen/imports" 27 | ) 28 | 29 | var ( 30 | pkg = flag.String("assert-path", "github.com/stretchr/testify/assert", "Path to the assert package") 31 | includeF = flag.Bool("include-format-funcs", false, "include format functions such as Errorf and Equalf") 32 | outputPkg = flag.String("output-package", "", "package for the resulting code") 33 | tmplFile = flag.String("template", "", "What file to load the function template from") 34 | out = flag.String("out", "", "What file to write the source code to") 35 | ) 36 | 37 | func main() { 38 | flag.Parse() 39 | 40 | scope, docs, err := parsePackageSource(*pkg) 41 | if err != nil { 42 | log.Fatal(err) 43 | } 44 | 45 | importer, funcs, err := analyzeCode(scope, docs) 46 | if err != nil { 47 | log.Fatal(err) 48 | } 49 | 50 | if err := generateCode(importer, funcs); err != nil { 51 | log.Fatal(err) 52 | } 53 | } 54 | 55 | func generateCode(importer imports.Importer, funcs []testFunc) error { 56 | buff := bytes.NewBuffer(nil) 57 | 58 | tmplHead, tmplFunc, err := parseTemplates() 59 | if err != nil { 60 | return err 61 | } 62 | 63 | // Generate header 64 | if err := tmplHead.Execute(buff, struct { 65 | Name string 66 | Imports map[string]string 67 | }{ 68 | *outputPkg, 69 | importer.Imports(), 70 | }); err != nil { 71 | return err 72 | } 73 | 74 | // Generate funcs 75 | for _, fn := range funcs { 76 | buff.Write([]byte("\n\n")) 77 | if err := tmplFunc.Execute(buff, &fn); err != nil { 78 | return err 79 | } 80 | } 81 | 82 | code, err := format.Source(buff.Bytes()) 83 | if err != nil { 84 | return err 85 | } 86 | 87 | // Write file 88 | output, err := outputFile() 89 | if err != nil { 90 | return err 91 | } 92 | defer output.Close() 93 | _, err = io.Copy(output, bytes.NewReader(code)) 94 | return err 95 | } 96 | 97 | func parseTemplates() (*template.Template, *template.Template, error) { 98 | tmplHead, err := template.New("header").Parse(headerTemplate) 99 | if err != nil { 100 | return nil, nil, err 101 | } 102 | if *tmplFile != "" { 103 | f, err := os.ReadFile(*tmplFile) 104 | if err != nil { 105 | return nil, nil, err 106 | } 107 | funcTemplate = string(f) 108 | } 109 | tmpl, err := template.New("function").Funcs(template.FuncMap{ 110 | "replace": strings.ReplaceAll, 111 | }).Parse(funcTemplate) 112 | if err != nil { 113 | return nil, nil, err 114 | } 115 | return tmplHead, tmpl, nil 116 | } 117 | 118 | func outputFile() (*os.File, error) { 119 | filename := *out 120 | if filename == "-" || (filename == "" && *tmplFile == "") { 121 | return os.Stdout, nil 122 | } 123 | if filename == "" { 124 | filename = strings.TrimSuffix(strings.TrimSuffix(*tmplFile, ".tmpl"), ".go") + ".go" 125 | } 126 | return os.Create(filename) 127 | } 128 | 129 | // analyzeCode takes the types scope and the docs and returns the import 130 | // information and information about all the assertion functions. 131 | func analyzeCode(scope *types.Scope, docs *doc.Package) (imports.Importer, []testFunc, error) { 132 | testingT := scope.Lookup("TestingT").Type().Underlying().(*types.Interface) 133 | 134 | importer := imports.New(*outputPkg) 135 | var funcs []testFunc 136 | // Go through all the top level functions 137 | for _, fdocs := range docs.Funcs { 138 | // Find the function 139 | obj := scope.Lookup(fdocs.Name) 140 | 141 | fn, ok := obj.(*types.Func) 142 | if !ok { 143 | continue 144 | } 145 | // Check function signature has at least two arguments 146 | sig := fn.Type().(*types.Signature) 147 | if sig.Params().Len() < 2 { 148 | continue 149 | } 150 | // Check first argument is of type testingT 151 | first, ok := sig.Params().At(0).Type().(*types.Named) 152 | if !ok { 153 | continue 154 | } 155 | firstType, ok := first.Underlying().(*types.Interface) 156 | if !ok { 157 | continue 158 | } 159 | if !types.Implements(firstType, testingT) { 160 | continue 161 | } 162 | 163 | // Skip functions ending with f 164 | if strings.HasSuffix(fdocs.Name, "f") && !*includeF { 165 | continue 166 | } 167 | 168 | funcs = append(funcs, testFunc{*outputPkg, fdocs, fn}) 169 | importer.AddImportsFrom(sig.Params()) 170 | } 171 | return importer, funcs, nil 172 | } 173 | 174 | // parsePackageSource returns the types scope and the package documentation from the package 175 | func parsePackageSource(pkg string) (*types.Scope, *doc.Package, error) { 176 | pd, err := build.Import(pkg, ".", 0) 177 | if err != nil { 178 | return nil, nil, err 179 | } 180 | 181 | fset := token.NewFileSet() 182 | files := make(map[string]*ast.File) 183 | fileList := make([]*ast.File, len(pd.GoFiles)) 184 | for i, fname := range pd.GoFiles { 185 | src, err := os.ReadFile(path.Join(pd.Dir, fname)) 186 | if err != nil { 187 | return nil, nil, err 188 | } 189 | f, err := parser.ParseFile(fset, fname, src, parser.ParseComments|parser.AllErrors) 190 | if err != nil { 191 | return nil, nil, err 192 | } 193 | files[fname] = f 194 | fileList[i] = f 195 | } 196 | 197 | cfg := types.Config{ 198 | Importer: importer.For("source", nil), 199 | } 200 | info := types.Info{ 201 | Defs: make(map[*ast.Ident]types.Object), 202 | } 203 | tp, err := cfg.Check(pkg, fset, fileList, &info) 204 | if err != nil { 205 | return nil, nil, err 206 | } 207 | 208 | scope := tp.Scope() 209 | 210 | ap, _ := ast.NewPackage(fset, files, nil, nil) 211 | docs := doc.New(ap, pkg, 0) 212 | 213 | return scope, docs, nil 214 | } 215 | 216 | type testFunc struct { 217 | CurrentPkg string 218 | DocInfo *doc.Func 219 | TypeInfo *types.Func 220 | } 221 | 222 | func (f *testFunc) Qualifier(p *types.Package) string { 223 | if p == nil || p.Name() == f.CurrentPkg { 224 | return "" 225 | } 226 | return p.Name() 227 | } 228 | 229 | func (f *testFunc) Params() string { 230 | sig := f.TypeInfo.Type().(*types.Signature) 231 | params := sig.Params() 232 | p := "" 233 | comma := "" 234 | to := params.Len() 235 | var i int 236 | 237 | if sig.Variadic() { 238 | to-- 239 | } 240 | for i = 1; i < to; i++ { 241 | param := params.At(i) 242 | p += fmt.Sprintf("%s%s %s", comma, param.Name(), types.TypeString(param.Type(), f.Qualifier)) 243 | comma = ", " 244 | } 245 | if sig.Variadic() { 246 | param := params.At(params.Len() - 1) 247 | p += fmt.Sprintf("%s%s ...%s", comma, param.Name(), types.TypeString(param.Type().(*types.Slice).Elem(), f.Qualifier)) 248 | } 249 | return p 250 | } 251 | 252 | func (f *testFunc) ForwardedParams() string { 253 | sig := f.TypeInfo.Type().(*types.Signature) 254 | params := sig.Params() 255 | p := "" 256 | comma := "" 257 | to := params.Len() 258 | var i int 259 | 260 | if sig.Variadic() { 261 | to-- 262 | } 263 | for i = 1; i < to; i++ { 264 | param := params.At(i) 265 | p += fmt.Sprintf("%s%s", comma, param.Name()) 266 | comma = ", " 267 | } 268 | if sig.Variadic() { 269 | param := params.At(params.Len() - 1) 270 | p += fmt.Sprintf("%s%s...", comma, param.Name()) 271 | } 272 | return p 273 | } 274 | 275 | func (f *testFunc) ParamsFormat() string { 276 | return strings.Replace(f.Params(), "msgAndArgs", "msg string, args", 1) 277 | } 278 | 279 | func (f *testFunc) ForwardedParamsFormat() string { 280 | return strings.Replace(f.ForwardedParams(), "msgAndArgs", "append([]interface{}{msg}, args...)", 1) 281 | } 282 | 283 | func (f *testFunc) Comment() string { 284 | return "// " + strings.Replace(strings.TrimSpace(f.DocInfo.Doc), "\n", "\n// ", -1) 285 | } 286 | 287 | func (f *testFunc) CommentFormat() string { 288 | search := fmt.Sprintf("%s", f.DocInfo.Name) 289 | replace := fmt.Sprintf("%sf", f.DocInfo.Name) 290 | comment := strings.Replace(f.Comment(), search, replace, -1) 291 | exp := regexp.MustCompile(replace + `\(((\(\)|[^\n])+)\)`) 292 | return exp.ReplaceAllString(comment, replace+`($1, "error message %s", "formatted")`) 293 | } 294 | 295 | func (f *testFunc) CommentWithoutT(receiver string) string { 296 | search := fmt.Sprintf("assert.%s(t, ", f.DocInfo.Name) 297 | replace := fmt.Sprintf("%s.%s(", receiver, f.DocInfo.Name) 298 | return strings.Replace(f.Comment(), search, replace, -1) 299 | } 300 | 301 | // Standard header https://go.dev/s/generatedcode. 302 | var headerTemplate = `// Code generated with github.com/stretchr/testify/_codegen; DO NOT EDIT. 303 | 304 | package {{.Name}} 305 | 306 | import ( 307 | {{range $path, $name := .Imports}} 308 | {{$name}} "{{$path}}"{{end}} 309 | ) 310 | ` 311 | 312 | var funcTemplate = `{{.Comment}} 313 | func (fwd *AssertionsForwarder) {{.DocInfo.Name}}({{.Params}}) bool { 314 | return assert.{{.DocInfo.Name}}({{.ForwardedParams}}) 315 | }` 316 | -------------------------------------------------------------------------------- /assert/assertion_compare.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "reflect" 7 | "time" 8 | ) 9 | 10 | // Deprecated: CompareType has only ever been for internal use and has accidentally been published since v1.6.0. Do not use it. 11 | type CompareType = compareResult 12 | 13 | type compareResult int 14 | 15 | const ( 16 | compareLess compareResult = iota - 1 17 | compareEqual 18 | compareGreater 19 | ) 20 | 21 | var ( 22 | intType = reflect.TypeOf(int(1)) 23 | int8Type = reflect.TypeOf(int8(1)) 24 | int16Type = reflect.TypeOf(int16(1)) 25 | int32Type = reflect.TypeOf(int32(1)) 26 | int64Type = reflect.TypeOf(int64(1)) 27 | 28 | uintType = reflect.TypeOf(uint(1)) 29 | uint8Type = reflect.TypeOf(uint8(1)) 30 | uint16Type = reflect.TypeOf(uint16(1)) 31 | uint32Type = reflect.TypeOf(uint32(1)) 32 | uint64Type = reflect.TypeOf(uint64(1)) 33 | 34 | uintptrType = reflect.TypeOf(uintptr(1)) 35 | 36 | float32Type = reflect.TypeOf(float32(1)) 37 | float64Type = reflect.TypeOf(float64(1)) 38 | 39 | stringType = reflect.TypeOf("") 40 | 41 | timeType = reflect.TypeOf(time.Time{}) 42 | bytesType = reflect.TypeOf([]byte{}) 43 | ) 44 | 45 | func compare(obj1, obj2 interface{}, kind reflect.Kind) (compareResult, bool) { 46 | obj1Value := reflect.ValueOf(obj1) 47 | obj2Value := reflect.ValueOf(obj2) 48 | 49 | // throughout this switch we try and avoid calling .Convert() if possible, 50 | // as this has a pretty big performance impact 51 | switch kind { 52 | case reflect.Int: 53 | { 54 | intobj1, ok := obj1.(int) 55 | if !ok { 56 | intobj1 = obj1Value.Convert(intType).Interface().(int) 57 | } 58 | intobj2, ok := obj2.(int) 59 | if !ok { 60 | intobj2 = obj2Value.Convert(intType).Interface().(int) 61 | } 62 | if intobj1 > intobj2 { 63 | return compareGreater, true 64 | } 65 | if intobj1 == intobj2 { 66 | return compareEqual, true 67 | } 68 | if intobj1 < intobj2 { 69 | return compareLess, true 70 | } 71 | } 72 | case reflect.Int8: 73 | { 74 | int8obj1, ok := obj1.(int8) 75 | if !ok { 76 | int8obj1 = obj1Value.Convert(int8Type).Interface().(int8) 77 | } 78 | int8obj2, ok := obj2.(int8) 79 | if !ok { 80 | int8obj2 = obj2Value.Convert(int8Type).Interface().(int8) 81 | } 82 | if int8obj1 > int8obj2 { 83 | return compareGreater, true 84 | } 85 | if int8obj1 == int8obj2 { 86 | return compareEqual, true 87 | } 88 | if int8obj1 < int8obj2 { 89 | return compareLess, true 90 | } 91 | } 92 | case reflect.Int16: 93 | { 94 | int16obj1, ok := obj1.(int16) 95 | if !ok { 96 | int16obj1 = obj1Value.Convert(int16Type).Interface().(int16) 97 | } 98 | int16obj2, ok := obj2.(int16) 99 | if !ok { 100 | int16obj2 = obj2Value.Convert(int16Type).Interface().(int16) 101 | } 102 | if int16obj1 > int16obj2 { 103 | return compareGreater, true 104 | } 105 | if int16obj1 == int16obj2 { 106 | return compareEqual, true 107 | } 108 | if int16obj1 < int16obj2 { 109 | return compareLess, true 110 | } 111 | } 112 | case reflect.Int32: 113 | { 114 | int32obj1, ok := obj1.(int32) 115 | if !ok { 116 | int32obj1 = obj1Value.Convert(int32Type).Interface().(int32) 117 | } 118 | int32obj2, ok := obj2.(int32) 119 | if !ok { 120 | int32obj2 = obj2Value.Convert(int32Type).Interface().(int32) 121 | } 122 | if int32obj1 > int32obj2 { 123 | return compareGreater, true 124 | } 125 | if int32obj1 == int32obj2 { 126 | return compareEqual, true 127 | } 128 | if int32obj1 < int32obj2 { 129 | return compareLess, true 130 | } 131 | } 132 | case reflect.Int64: 133 | { 134 | int64obj1, ok := obj1.(int64) 135 | if !ok { 136 | int64obj1 = obj1Value.Convert(int64Type).Interface().(int64) 137 | } 138 | int64obj2, ok := obj2.(int64) 139 | if !ok { 140 | int64obj2 = obj2Value.Convert(int64Type).Interface().(int64) 141 | } 142 | if int64obj1 > int64obj2 { 143 | return compareGreater, true 144 | } 145 | if int64obj1 == int64obj2 { 146 | return compareEqual, true 147 | } 148 | if int64obj1 < int64obj2 { 149 | return compareLess, true 150 | } 151 | } 152 | case reflect.Uint: 153 | { 154 | uintobj1, ok := obj1.(uint) 155 | if !ok { 156 | uintobj1 = obj1Value.Convert(uintType).Interface().(uint) 157 | } 158 | uintobj2, ok := obj2.(uint) 159 | if !ok { 160 | uintobj2 = obj2Value.Convert(uintType).Interface().(uint) 161 | } 162 | if uintobj1 > uintobj2 { 163 | return compareGreater, true 164 | } 165 | if uintobj1 == uintobj2 { 166 | return compareEqual, true 167 | } 168 | if uintobj1 < uintobj2 { 169 | return compareLess, true 170 | } 171 | } 172 | case reflect.Uint8: 173 | { 174 | uint8obj1, ok := obj1.(uint8) 175 | if !ok { 176 | uint8obj1 = obj1Value.Convert(uint8Type).Interface().(uint8) 177 | } 178 | uint8obj2, ok := obj2.(uint8) 179 | if !ok { 180 | uint8obj2 = obj2Value.Convert(uint8Type).Interface().(uint8) 181 | } 182 | if uint8obj1 > uint8obj2 { 183 | return compareGreater, true 184 | } 185 | if uint8obj1 == uint8obj2 { 186 | return compareEqual, true 187 | } 188 | if uint8obj1 < uint8obj2 { 189 | return compareLess, true 190 | } 191 | } 192 | case reflect.Uint16: 193 | { 194 | uint16obj1, ok := obj1.(uint16) 195 | if !ok { 196 | uint16obj1 = obj1Value.Convert(uint16Type).Interface().(uint16) 197 | } 198 | uint16obj2, ok := obj2.(uint16) 199 | if !ok { 200 | uint16obj2 = obj2Value.Convert(uint16Type).Interface().(uint16) 201 | } 202 | if uint16obj1 > uint16obj2 { 203 | return compareGreater, true 204 | } 205 | if uint16obj1 == uint16obj2 { 206 | return compareEqual, true 207 | } 208 | if uint16obj1 < uint16obj2 { 209 | return compareLess, true 210 | } 211 | } 212 | case reflect.Uint32: 213 | { 214 | uint32obj1, ok := obj1.(uint32) 215 | if !ok { 216 | uint32obj1 = obj1Value.Convert(uint32Type).Interface().(uint32) 217 | } 218 | uint32obj2, ok := obj2.(uint32) 219 | if !ok { 220 | uint32obj2 = obj2Value.Convert(uint32Type).Interface().(uint32) 221 | } 222 | if uint32obj1 > uint32obj2 { 223 | return compareGreater, true 224 | } 225 | if uint32obj1 == uint32obj2 { 226 | return compareEqual, true 227 | } 228 | if uint32obj1 < uint32obj2 { 229 | return compareLess, true 230 | } 231 | } 232 | case reflect.Uint64: 233 | { 234 | uint64obj1, ok := obj1.(uint64) 235 | if !ok { 236 | uint64obj1 = obj1Value.Convert(uint64Type).Interface().(uint64) 237 | } 238 | uint64obj2, ok := obj2.(uint64) 239 | if !ok { 240 | uint64obj2 = obj2Value.Convert(uint64Type).Interface().(uint64) 241 | } 242 | if uint64obj1 > uint64obj2 { 243 | return compareGreater, true 244 | } 245 | if uint64obj1 == uint64obj2 { 246 | return compareEqual, true 247 | } 248 | if uint64obj1 < uint64obj2 { 249 | return compareLess, true 250 | } 251 | } 252 | case reflect.Float32: 253 | { 254 | float32obj1, ok := obj1.(float32) 255 | if !ok { 256 | float32obj1 = obj1Value.Convert(float32Type).Interface().(float32) 257 | } 258 | float32obj2, ok := obj2.(float32) 259 | if !ok { 260 | float32obj2 = obj2Value.Convert(float32Type).Interface().(float32) 261 | } 262 | if float32obj1 > float32obj2 { 263 | return compareGreater, true 264 | } 265 | if float32obj1 == float32obj2 { 266 | return compareEqual, true 267 | } 268 | if float32obj1 < float32obj2 { 269 | return compareLess, true 270 | } 271 | } 272 | case reflect.Float64: 273 | { 274 | float64obj1, ok := obj1.(float64) 275 | if !ok { 276 | float64obj1 = obj1Value.Convert(float64Type).Interface().(float64) 277 | } 278 | float64obj2, ok := obj2.(float64) 279 | if !ok { 280 | float64obj2 = obj2Value.Convert(float64Type).Interface().(float64) 281 | } 282 | if float64obj1 > float64obj2 { 283 | return compareGreater, true 284 | } 285 | if float64obj1 == float64obj2 { 286 | return compareEqual, true 287 | } 288 | if float64obj1 < float64obj2 { 289 | return compareLess, true 290 | } 291 | } 292 | case reflect.String: 293 | { 294 | stringobj1, ok := obj1.(string) 295 | if !ok { 296 | stringobj1 = obj1Value.Convert(stringType).Interface().(string) 297 | } 298 | stringobj2, ok := obj2.(string) 299 | if !ok { 300 | stringobj2 = obj2Value.Convert(stringType).Interface().(string) 301 | } 302 | if stringobj1 > stringobj2 { 303 | return compareGreater, true 304 | } 305 | if stringobj1 == stringobj2 { 306 | return compareEqual, true 307 | } 308 | if stringobj1 < stringobj2 { 309 | return compareLess, true 310 | } 311 | } 312 | // Check for known struct types we can check for compare results. 313 | case reflect.Struct: 314 | { 315 | // All structs enter here. We're not interested in most types. 316 | if !obj1Value.CanConvert(timeType) { 317 | break 318 | } 319 | 320 | // time.Time can be compared! 321 | timeObj1, ok := obj1.(time.Time) 322 | if !ok { 323 | timeObj1 = obj1Value.Convert(timeType).Interface().(time.Time) 324 | } 325 | 326 | timeObj2, ok := obj2.(time.Time) 327 | if !ok { 328 | timeObj2 = obj2Value.Convert(timeType).Interface().(time.Time) 329 | } 330 | 331 | if timeObj1.Before(timeObj2) { 332 | return compareLess, true 333 | } 334 | if timeObj1.Equal(timeObj2) { 335 | return compareEqual, true 336 | } 337 | return compareGreater, true 338 | } 339 | case reflect.Slice: 340 | { 341 | // We only care about the []byte type. 342 | if !obj1Value.CanConvert(bytesType) { 343 | break 344 | } 345 | 346 | // []byte can be compared! 347 | bytesObj1, ok := obj1.([]byte) 348 | if !ok { 349 | bytesObj1 = obj1Value.Convert(bytesType).Interface().([]byte) 350 | 351 | } 352 | bytesObj2, ok := obj2.([]byte) 353 | if !ok { 354 | bytesObj2 = obj2Value.Convert(bytesType).Interface().([]byte) 355 | } 356 | 357 | return compareResult(bytes.Compare(bytesObj1, bytesObj2)), true 358 | } 359 | case reflect.Uintptr: 360 | { 361 | uintptrObj1, ok := obj1.(uintptr) 362 | if !ok { 363 | uintptrObj1 = obj1Value.Convert(uintptrType).Interface().(uintptr) 364 | } 365 | uintptrObj2, ok := obj2.(uintptr) 366 | if !ok { 367 | uintptrObj2 = obj2Value.Convert(uintptrType).Interface().(uintptr) 368 | } 369 | if uintptrObj1 > uintptrObj2 { 370 | return compareGreater, true 371 | } 372 | if uintptrObj1 == uintptrObj2 { 373 | return compareEqual, true 374 | } 375 | if uintptrObj1 < uintptrObj2 { 376 | return compareLess, true 377 | } 378 | } 379 | } 380 | 381 | return compareEqual, false 382 | } 383 | 384 | // Greater asserts that the first element is greater than the second 385 | // 386 | // assert.Greater(t, 2, 1) 387 | // assert.Greater(t, float64(2), float64(1)) 388 | // assert.Greater(t, "b", "a") 389 | func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { 390 | if h, ok := t.(tHelper); ok { 391 | h.Helper() 392 | } 393 | failMessage := fmt.Sprintf("\"%v\" is not greater than \"%v\"", e1, e2) 394 | return compareTwoValues(t, e1, e2, []compareResult{compareGreater}, failMessage, msgAndArgs...) 395 | } 396 | 397 | // GreaterOrEqual asserts that the first element is greater than or equal to the second 398 | // 399 | // assert.GreaterOrEqual(t, 2, 1) 400 | // assert.GreaterOrEqual(t, 2, 2) 401 | // assert.GreaterOrEqual(t, "b", "a") 402 | // assert.GreaterOrEqual(t, "b", "b") 403 | func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { 404 | if h, ok := t.(tHelper); ok { 405 | h.Helper() 406 | } 407 | failMessage := fmt.Sprintf("\"%v\" is not greater than or equal to \"%v\"", e1, e2) 408 | return compareTwoValues(t, e1, e2, []compareResult{compareGreater, compareEqual}, failMessage, msgAndArgs...) 409 | } 410 | 411 | // Less asserts that the first element is less than the second 412 | // 413 | // assert.Less(t, 1, 2) 414 | // assert.Less(t, float64(1), float64(2)) 415 | // assert.Less(t, "a", "b") 416 | func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { 417 | if h, ok := t.(tHelper); ok { 418 | h.Helper() 419 | } 420 | failMessage := fmt.Sprintf("\"%v\" is not less than \"%v\"", e1, e2) 421 | return compareTwoValues(t, e1, e2, []compareResult{compareLess}, failMessage, msgAndArgs...) 422 | } 423 | 424 | // LessOrEqual asserts that the first element is less than or equal to the second 425 | // 426 | // assert.LessOrEqual(t, 1, 2) 427 | // assert.LessOrEqual(t, 2, 2) 428 | // assert.LessOrEqual(t, "a", "b") 429 | // assert.LessOrEqual(t, "b", "b") 430 | func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { 431 | if h, ok := t.(tHelper); ok { 432 | h.Helper() 433 | } 434 | failMessage := fmt.Sprintf("\"%v\" is not less than or equal to \"%v\"", e1, e2) 435 | return compareTwoValues(t, e1, e2, []compareResult{compareLess, compareEqual}, failMessage, msgAndArgs...) 436 | } 437 | 438 | // Positive asserts that the specified element is positive 439 | // 440 | // assert.Positive(t, 1) 441 | // assert.Positive(t, 1.23) 442 | func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { 443 | if h, ok := t.(tHelper); ok { 444 | h.Helper() 445 | } 446 | zero := reflect.Zero(reflect.TypeOf(e)) 447 | failMessage := fmt.Sprintf("\"%v\" is not positive", e) 448 | return compareTwoValues(t, e, zero.Interface(), []compareResult{compareGreater}, failMessage, msgAndArgs...) 449 | } 450 | 451 | // Negative asserts that the specified element is negative 452 | // 453 | // assert.Negative(t, -1) 454 | // assert.Negative(t, -1.23) 455 | func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { 456 | if h, ok := t.(tHelper); ok { 457 | h.Helper() 458 | } 459 | zero := reflect.Zero(reflect.TypeOf(e)) 460 | failMessage := fmt.Sprintf("\"%v\" is not negative", e) 461 | return compareTwoValues(t, e, zero.Interface(), []compareResult{compareLess}, failMessage, msgAndArgs...) 462 | } 463 | 464 | func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...interface{}) bool { 465 | if h, ok := t.(tHelper); ok { 466 | h.Helper() 467 | } 468 | 469 | e1Kind := reflect.ValueOf(e1).Kind() 470 | e2Kind := reflect.ValueOf(e2).Kind() 471 | if e1Kind != e2Kind { 472 | return Fail(t, "Elements should be the same type", msgAndArgs...) 473 | } 474 | 475 | compareResult, isComparable := compare(e1, e2, e1Kind) 476 | if !isComparable { 477 | return Fail(t, fmt.Sprintf(`Can not compare type "%T"`, e1), msgAndArgs...) 478 | } 479 | 480 | if !containsValue(allowedComparesResults, compareResult) { 481 | return Fail(t, failMessage, msgAndArgs...) 482 | } 483 | 484 | return true 485 | } 486 | 487 | func containsValue(values []compareResult, value compareResult) bool { 488 | for _, v := range values { 489 | if v == value { 490 | return true 491 | } 492 | } 493 | 494 | return false 495 | } 496 | -------------------------------------------------------------------------------- /assert/assertion_compare_test.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "reflect" 7 | "runtime" 8 | "testing" 9 | "time" 10 | ) 11 | 12 | func TestCompare(t *testing.T) { 13 | t.Parallel() 14 | 15 | type customString string 16 | type customInt int 17 | type customInt8 int8 18 | type customInt16 int16 19 | type customInt32 int32 20 | type customInt64 int64 21 | type customUInt uint 22 | type customUInt8 uint8 23 | type customUInt16 uint16 24 | type customUInt32 uint32 25 | type customUInt64 uint64 26 | type customFloat32 float32 27 | type customFloat64 float64 28 | type customUintptr uintptr 29 | type customTime time.Time 30 | type customBytes []byte 31 | for _, currCase := range []struct { 32 | less interface{} 33 | greater interface{} 34 | cType string 35 | }{ 36 | {less: customString("a"), greater: customString("b"), cType: "string"}, 37 | {less: "a", greater: "b", cType: "string"}, 38 | {less: customInt(1), greater: customInt(2), cType: "int"}, 39 | {less: int(1), greater: int(2), cType: "int"}, 40 | {less: customInt8(1), greater: customInt8(2), cType: "int8"}, 41 | {less: int8(1), greater: int8(2), cType: "int8"}, 42 | {less: customInt16(1), greater: customInt16(2), cType: "int16"}, 43 | {less: int16(1), greater: int16(2), cType: "int16"}, 44 | {less: customInt32(1), greater: customInt32(2), cType: "int32"}, 45 | {less: int32(1), greater: int32(2), cType: "int32"}, 46 | {less: customInt64(1), greater: customInt64(2), cType: "int64"}, 47 | {less: int64(1), greater: int64(2), cType: "int64"}, 48 | {less: customUInt(1), greater: customUInt(2), cType: "uint"}, 49 | {less: uint8(1), greater: uint8(2), cType: "uint8"}, 50 | {less: customUInt8(1), greater: customUInt8(2), cType: "uint8"}, 51 | {less: uint16(1), greater: uint16(2), cType: "uint16"}, 52 | {less: customUInt16(1), greater: customUInt16(2), cType: "uint16"}, 53 | {less: uint32(1), greater: uint32(2), cType: "uint32"}, 54 | {less: customUInt32(1), greater: customUInt32(2), cType: "uint32"}, 55 | {less: uint64(1), greater: uint64(2), cType: "uint64"}, 56 | {less: customUInt64(1), greater: customUInt64(2), cType: "uint64"}, 57 | {less: float32(1.23), greater: float32(2.34), cType: "float32"}, 58 | {less: customFloat32(1.23), greater: customFloat32(2.23), cType: "float32"}, 59 | {less: float64(1.23), greater: float64(2.34), cType: "float64"}, 60 | {less: customFloat64(1.23), greater: customFloat64(2.34), cType: "float64"}, 61 | {less: uintptr(1), greater: uintptr(2), cType: "uintptr"}, 62 | {less: customUintptr(1), greater: customUintptr(2), cType: "uint64"}, 63 | {less: time.Now(), greater: time.Now().Add(time.Hour), cType: "time.Time"}, 64 | {less: time.Date(2024, 0, 0, 0, 0, 0, 0, time.Local), greater: time.Date(2263, 0, 0, 0, 0, 0, 0, time.Local), cType: "time.Time"}, 65 | {less: customTime(time.Now()), greater: customTime(time.Now().Add(time.Hour)), cType: "time.Time"}, 66 | {less: []byte{1, 1}, greater: []byte{1, 2}, cType: "[]byte"}, 67 | {less: customBytes([]byte{1, 1}), greater: customBytes([]byte{1, 2}), cType: "[]byte"}, 68 | } { 69 | resLess, isComparable := compare(currCase.less, currCase.greater, reflect.ValueOf(currCase.less).Kind()) 70 | if !isComparable { 71 | t.Error("object should be comparable for type " + currCase.cType) 72 | } 73 | 74 | if resLess != compareLess { 75 | t.Errorf("object less (%v) should be less than greater (%v) for type "+currCase.cType, 76 | currCase.less, currCase.greater) 77 | } 78 | 79 | resGreater, isComparable := compare(currCase.greater, currCase.less, reflect.ValueOf(currCase.less).Kind()) 80 | if !isComparable { 81 | t.Error("object are comparable for type " + currCase.cType) 82 | } 83 | 84 | if resGreater != compareGreater { 85 | t.Errorf("object greater should be greater than less for type " + currCase.cType) 86 | } 87 | 88 | resEqual, isComparable := compare(currCase.less, currCase.less, reflect.ValueOf(currCase.less).Kind()) 89 | if !isComparable { 90 | t.Error("object are comparable for type " + currCase.cType) 91 | } 92 | 93 | if resEqual != 0 { 94 | t.Errorf("objects should be equal for type " + currCase.cType) 95 | } 96 | } 97 | } 98 | 99 | type outputT struct { 100 | buf *bytes.Buffer 101 | helpers map[string]struct{} 102 | } 103 | 104 | // Implements TestingT 105 | func (t *outputT) Errorf(format string, args ...interface{}) { 106 | s := fmt.Sprintf(format, args...) 107 | t.buf.WriteString(s) 108 | } 109 | 110 | func (t *outputT) Helper() { 111 | if t.helpers == nil { 112 | t.helpers = make(map[string]struct{}) 113 | } 114 | t.helpers[callerName(1)] = struct{}{} 115 | } 116 | 117 | // callerName gives the function name (qualified with a package path) 118 | // for the caller after skip frames (where 0 means the current function). 119 | func callerName(skip int) string { 120 | // Make room for the skip PC. 121 | var pc [1]uintptr 122 | n := runtime.Callers(skip+2, pc[:]) // skip + runtime.Callers + callerName 123 | if n == 0 { 124 | panic("testing: zero callers found") 125 | } 126 | frames := runtime.CallersFrames(pc[:n]) 127 | frame, _ := frames.Next() 128 | return frame.Function 129 | } 130 | 131 | func TestGreater(t *testing.T) { 132 | t.Parallel() 133 | 134 | mockT := new(testing.T) 135 | 136 | if !Greater(mockT, 2, 1) { 137 | t.Error("Greater should return true") 138 | } 139 | 140 | if Greater(mockT, 1, 1) { 141 | t.Error("Greater should return false") 142 | } 143 | 144 | if Greater(mockT, 1, 2) { 145 | t.Error("Greater should return false") 146 | } 147 | 148 | // Check error report 149 | for _, currCase := range []struct { 150 | less interface{} 151 | greater interface{} 152 | msg string 153 | }{ 154 | {less: "a", greater: "b", msg: `"a" is not greater than "b"`}, 155 | {less: int(1), greater: int(2), msg: `"1" is not greater than "2"`}, 156 | {less: int8(1), greater: int8(2), msg: `"1" is not greater than "2"`}, 157 | {less: int16(1), greater: int16(2), msg: `"1" is not greater than "2"`}, 158 | {less: int32(1), greater: int32(2), msg: `"1" is not greater than "2"`}, 159 | {less: int64(1), greater: int64(2), msg: `"1" is not greater than "2"`}, 160 | {less: uint8(1), greater: uint8(2), msg: `"1" is not greater than "2"`}, 161 | {less: uint16(1), greater: uint16(2), msg: `"1" is not greater than "2"`}, 162 | {less: uint32(1), greater: uint32(2), msg: `"1" is not greater than "2"`}, 163 | {less: uint64(1), greater: uint64(2), msg: `"1" is not greater than "2"`}, 164 | {less: float32(1.23), greater: float32(2.34), msg: `"1.23" is not greater than "2.34"`}, 165 | {less: float64(1.23), greater: float64(2.34), msg: `"1.23" is not greater than "2.34"`}, 166 | {less: uintptr(1), greater: uintptr(2), msg: `"1" is not greater than "2"`}, 167 | {less: time.Time{}, greater: time.Time{}.Add(time.Hour), msg: `"0001-01-01 00:00:00 +0000 UTC" is not greater than "0001-01-01 01:00:00 +0000 UTC"`}, 168 | {less: []byte{1, 1}, greater: []byte{1, 2}, msg: `"[1 1]" is not greater than "[1 2]"`}, 169 | } { 170 | out := &outputT{buf: bytes.NewBuffer(nil)} 171 | False(t, Greater(out, currCase.less, currCase.greater)) 172 | Contains(t, out.buf.String(), currCase.msg) 173 | Contains(t, out.helpers, "github.com/stretchr/testify/assert.Greater") 174 | } 175 | } 176 | 177 | func TestGreaterOrEqual(t *testing.T) { 178 | t.Parallel() 179 | 180 | mockT := new(testing.T) 181 | 182 | if !GreaterOrEqual(mockT, 2, 1) { 183 | t.Error("GreaterOrEqual should return true") 184 | } 185 | 186 | if !GreaterOrEqual(mockT, 1, 1) { 187 | t.Error("GreaterOrEqual should return true") 188 | } 189 | 190 | if GreaterOrEqual(mockT, 1, 2) { 191 | t.Error("GreaterOrEqual should return false") 192 | } 193 | 194 | // Check error report 195 | for _, currCase := range []struct { 196 | less interface{} 197 | greater interface{} 198 | msg string 199 | }{ 200 | {less: "a", greater: "b", msg: `"a" is not greater than or equal to "b"`}, 201 | {less: int(1), greater: int(2), msg: `"1" is not greater than or equal to "2"`}, 202 | {less: int8(1), greater: int8(2), msg: `"1" is not greater than or equal to "2"`}, 203 | {less: int16(1), greater: int16(2), msg: `"1" is not greater than or equal to "2"`}, 204 | {less: int32(1), greater: int32(2), msg: `"1" is not greater than or equal to "2"`}, 205 | {less: int64(1), greater: int64(2), msg: `"1" is not greater than or equal to "2"`}, 206 | {less: uint8(1), greater: uint8(2), msg: `"1" is not greater than or equal to "2"`}, 207 | {less: uint16(1), greater: uint16(2), msg: `"1" is not greater than or equal to "2"`}, 208 | {less: uint32(1), greater: uint32(2), msg: `"1" is not greater than or equal to "2"`}, 209 | {less: uint64(1), greater: uint64(2), msg: `"1" is not greater than or equal to "2"`}, 210 | {less: float32(1.23), greater: float32(2.34), msg: `"1.23" is not greater than or equal to "2.34"`}, 211 | {less: float64(1.23), greater: float64(2.34), msg: `"1.23" is not greater than or equal to "2.34"`}, 212 | {less: uintptr(1), greater: uintptr(2), msg: `"1" is not greater than or equal to "2"`}, 213 | {less: time.Time{}, greater: time.Time{}.Add(time.Hour), msg: `"0001-01-01 00:00:00 +0000 UTC" is not greater than or equal to "0001-01-01 01:00:00 +0000 UTC"`}, 214 | {less: []byte{1, 1}, greater: []byte{1, 2}, msg: `"[1 1]" is not greater than or equal to "[1 2]"`}, 215 | } { 216 | out := &outputT{buf: bytes.NewBuffer(nil)} 217 | False(t, GreaterOrEqual(out, currCase.less, currCase.greater)) 218 | Contains(t, out.buf.String(), currCase.msg) 219 | Contains(t, out.helpers, "github.com/stretchr/testify/assert.GreaterOrEqual") 220 | } 221 | } 222 | 223 | func TestLess(t *testing.T) { 224 | t.Parallel() 225 | 226 | mockT := new(testing.T) 227 | 228 | if !Less(mockT, 1, 2) { 229 | t.Error("Less should return true") 230 | } 231 | 232 | if Less(mockT, 1, 1) { 233 | t.Error("Less should return false") 234 | } 235 | 236 | if Less(mockT, 2, 1) { 237 | t.Error("Less should return false") 238 | } 239 | 240 | // Check error report 241 | for _, currCase := range []struct { 242 | less interface{} 243 | greater interface{} 244 | msg string 245 | }{ 246 | {less: "a", greater: "b", msg: `"b" is not less than "a"`}, 247 | {less: int(1), greater: int(2), msg: `"2" is not less than "1"`}, 248 | {less: int8(1), greater: int8(2), msg: `"2" is not less than "1"`}, 249 | {less: int16(1), greater: int16(2), msg: `"2" is not less than "1"`}, 250 | {less: int32(1), greater: int32(2), msg: `"2" is not less than "1"`}, 251 | {less: int64(1), greater: int64(2), msg: `"2" is not less than "1"`}, 252 | {less: uint8(1), greater: uint8(2), msg: `"2" is not less than "1"`}, 253 | {less: uint16(1), greater: uint16(2), msg: `"2" is not less than "1"`}, 254 | {less: uint32(1), greater: uint32(2), msg: `"2" is not less than "1"`}, 255 | {less: uint64(1), greater: uint64(2), msg: `"2" is not less than "1"`}, 256 | {less: float32(1.23), greater: float32(2.34), msg: `"2.34" is not less than "1.23"`}, 257 | {less: float64(1.23), greater: float64(2.34), msg: `"2.34" is not less than "1.23"`}, 258 | {less: uintptr(1), greater: uintptr(2), msg: `"2" is not less than "1"`}, 259 | {less: time.Time{}, greater: time.Time{}.Add(time.Hour), msg: `"0001-01-01 01:00:00 +0000 UTC" is not less than "0001-01-01 00:00:00 +0000 UTC"`}, 260 | {less: []byte{1, 1}, greater: []byte{1, 2}, msg: `"[1 2]" is not less than "[1 1]"`}, 261 | } { 262 | out := &outputT{buf: bytes.NewBuffer(nil)} 263 | False(t, Less(out, currCase.greater, currCase.less)) 264 | Contains(t, out.buf.String(), currCase.msg) 265 | Contains(t, out.helpers, "github.com/stretchr/testify/assert.Less") 266 | } 267 | } 268 | 269 | func TestLessOrEqual(t *testing.T) { 270 | t.Parallel() 271 | 272 | mockT := new(testing.T) 273 | 274 | if !LessOrEqual(mockT, 1, 2) { 275 | t.Error("LessOrEqual should return true") 276 | } 277 | 278 | if !LessOrEqual(mockT, 1, 1) { 279 | t.Error("LessOrEqual should return true") 280 | } 281 | 282 | if LessOrEqual(mockT, 2, 1) { 283 | t.Error("LessOrEqual should return false") 284 | } 285 | 286 | // Check error report 287 | for _, currCase := range []struct { 288 | less interface{} 289 | greater interface{} 290 | msg string 291 | }{ 292 | {less: "a", greater: "b", msg: `"b" is not less than or equal to "a"`}, 293 | {less: int(1), greater: int(2), msg: `"2" is not less than or equal to "1"`}, 294 | {less: int8(1), greater: int8(2), msg: `"2" is not less than or equal to "1"`}, 295 | {less: int16(1), greater: int16(2), msg: `"2" is not less than or equal to "1"`}, 296 | {less: int32(1), greater: int32(2), msg: `"2" is not less than or equal to "1"`}, 297 | {less: int64(1), greater: int64(2), msg: `"2" is not less than or equal to "1"`}, 298 | {less: uint8(1), greater: uint8(2), msg: `"2" is not less than or equal to "1"`}, 299 | {less: uint16(1), greater: uint16(2), msg: `"2" is not less than or equal to "1"`}, 300 | {less: uint32(1), greater: uint32(2), msg: `"2" is not less than or equal to "1"`}, 301 | {less: uint64(1), greater: uint64(2), msg: `"2" is not less than or equal to "1"`}, 302 | {less: float32(1.23), greater: float32(2.34), msg: `"2.34" is not less than or equal to "1.23"`}, 303 | {less: float64(1.23), greater: float64(2.34), msg: `"2.34" is not less than or equal to "1.23"`}, 304 | {less: uintptr(1), greater: uintptr(2), msg: `"2" is not less than or equal to "1"`}, 305 | {less: time.Time{}, greater: time.Time{}.Add(time.Hour), msg: `"0001-01-01 01:00:00 +0000 UTC" is not less than or equal to "0001-01-01 00:00:00 +0000 UTC"`}, 306 | {less: []byte{1, 1}, greater: []byte{1, 2}, msg: `"[1 2]" is not less than or equal to "[1 1]"`}, 307 | } { 308 | out := &outputT{buf: bytes.NewBuffer(nil)} 309 | False(t, LessOrEqual(out, currCase.greater, currCase.less)) 310 | Contains(t, out.buf.String(), currCase.msg) 311 | Contains(t, out.helpers, "github.com/stretchr/testify/assert.LessOrEqual") 312 | } 313 | } 314 | 315 | func TestPositive(t *testing.T) { 316 | t.Parallel() 317 | 318 | mockT := new(testing.T) 319 | 320 | if !Positive(mockT, 1) { 321 | t.Error("Positive should return true") 322 | } 323 | 324 | if !Positive(mockT, 1.23) { 325 | t.Error("Positive should return true") 326 | } 327 | 328 | if Positive(mockT, -1) { 329 | t.Error("Positive should return false") 330 | } 331 | 332 | if Positive(mockT, -1.23) { 333 | t.Error("Positive should return false") 334 | } 335 | 336 | // Check error report 337 | for _, currCase := range []struct { 338 | e interface{} 339 | msg string 340 | }{ 341 | {e: int(-1), msg: `"-1" is not positive`}, 342 | {e: int8(-1), msg: `"-1" is not positive`}, 343 | {e: int16(-1), msg: `"-1" is not positive`}, 344 | {e: int32(-1), msg: `"-1" is not positive`}, 345 | {e: int64(-1), msg: `"-1" is not positive`}, 346 | {e: float32(-1.23), msg: `"-1.23" is not positive`}, 347 | {e: float64(-1.23), msg: `"-1.23" is not positive`}, 348 | } { 349 | out := &outputT{buf: bytes.NewBuffer(nil)} 350 | False(t, Positive(out, currCase.e)) 351 | Contains(t, out.buf.String(), currCase.msg) 352 | Contains(t, out.helpers, "github.com/stretchr/testify/assert.Positive") 353 | } 354 | } 355 | 356 | func TestNegative(t *testing.T) { 357 | t.Parallel() 358 | 359 | mockT := new(testing.T) 360 | 361 | if !Negative(mockT, -1) { 362 | t.Error("Negative should return true") 363 | } 364 | 365 | if !Negative(mockT, -1.23) { 366 | t.Error("Negative should return true") 367 | } 368 | 369 | if Negative(mockT, 1) { 370 | t.Error("Negative should return false") 371 | } 372 | 373 | if Negative(mockT, 1.23) { 374 | t.Error("Negative should return false") 375 | } 376 | 377 | // Check error report 378 | for _, currCase := range []struct { 379 | e interface{} 380 | msg string 381 | }{ 382 | {e: int(1), msg: `"1" is not negative`}, 383 | {e: int8(1), msg: `"1" is not negative`}, 384 | {e: int16(1), msg: `"1" is not negative`}, 385 | {e: int32(1), msg: `"1" is not negative`}, 386 | {e: int64(1), msg: `"1" is not negative`}, 387 | {e: float32(1.23), msg: `"1.23" is not negative`}, 388 | {e: float64(1.23), msg: `"1.23" is not negative`}, 389 | } { 390 | out := &outputT{buf: bytes.NewBuffer(nil)} 391 | False(t, Negative(out, currCase.e)) 392 | Contains(t, out.buf.String(), currCase.msg) 393 | Contains(t, out.helpers, "github.com/stretchr/testify/assert.Negative") 394 | } 395 | } 396 | 397 | func Test_compareTwoValuesDifferentValuesTypes(t *testing.T) { 398 | t.Parallel() 399 | 400 | mockT := new(testing.T) 401 | 402 | for _, currCase := range []struct { 403 | v1 interface{} 404 | v2 interface{} 405 | compareResult bool 406 | }{ 407 | {v1: 123, v2: "abc"}, 408 | {v1: "abc", v2: 123456}, 409 | {v1: float64(12), v2: "123"}, 410 | {v1: "float(12)", v2: float64(1)}, 411 | } { 412 | result := compareTwoValues(mockT, currCase.v1, currCase.v2, []compareResult{compareLess, compareEqual, compareGreater}, "testFailMessage") 413 | False(t, result) 414 | } 415 | } 416 | 417 | func Test_compareTwoValuesNotComparableValues(t *testing.T) { 418 | t.Parallel() 419 | 420 | mockT := new(testing.T) 421 | 422 | type CompareStruct struct { 423 | } 424 | 425 | for _, currCase := range []struct { 426 | v1 interface{} 427 | v2 interface{} 428 | }{ 429 | {v1: CompareStruct{}, v2: CompareStruct{}}, 430 | {v1: map[string]int{}, v2: map[string]int{}}, 431 | {v1: make([]int, 5), v2: make([]int, 5)}, 432 | } { 433 | result := compareTwoValues(mockT, currCase.v1, currCase.v2, []compareResult{compareLess, compareEqual, compareGreater}, "testFailMessage") 434 | False(t, result) 435 | } 436 | } 437 | 438 | func Test_compareTwoValuesCorrectCompareResult(t *testing.T) { 439 | t.Parallel() 440 | 441 | mockT := new(testing.T) 442 | 443 | for _, currCase := range []struct { 444 | v1 interface{} 445 | v2 interface{} 446 | allowedResults []compareResult 447 | }{ 448 | {v1: 1, v2: 2, allowedResults: []compareResult{compareLess}}, 449 | {v1: 1, v2: 2, allowedResults: []compareResult{compareLess, compareEqual}}, 450 | {v1: 2, v2: 2, allowedResults: []compareResult{compareGreater, compareEqual}}, 451 | {v1: 2, v2: 2, allowedResults: []compareResult{compareEqual}}, 452 | {v1: 2, v2: 1, allowedResults: []compareResult{compareEqual, compareGreater}}, 453 | {v1: 2, v2: 1, allowedResults: []compareResult{compareGreater}}, 454 | } { 455 | result := compareTwoValues(mockT, currCase.v1, currCase.v2, currCase.allowedResults, "testFailMessage") 456 | True(t, result) 457 | } 458 | } 459 | 460 | func Test_containsValue(t *testing.T) { 461 | t.Parallel() 462 | 463 | for _, currCase := range []struct { 464 | values []compareResult 465 | value compareResult 466 | result bool 467 | }{ 468 | {values: []compareResult{compareGreater}, value: compareGreater, result: true}, 469 | {values: []compareResult{compareGreater, compareLess}, value: compareGreater, result: true}, 470 | {values: []compareResult{compareGreater, compareLess}, value: compareLess, result: true}, 471 | {values: []compareResult{compareGreater, compareLess}, value: compareEqual, result: false}, 472 | } { 473 | result := containsValue(currCase.values, currCase.value) 474 | Equal(t, currCase.result, result) 475 | } 476 | } 477 | 478 | func TestComparingMsgAndArgsForwarding(t *testing.T) { 479 | msgAndArgs := []interface{}{"format %s %x", "this", 0xc001} 480 | expectedOutput := "format this c001\n" 481 | funcs := []func(t TestingT){ 482 | func(t TestingT) { Greater(t, 1, 2, msgAndArgs...) }, 483 | func(t TestingT) { GreaterOrEqual(t, 1, 2, msgAndArgs...) }, 484 | func(t TestingT) { Less(t, 2, 1, msgAndArgs...) }, 485 | func(t TestingT) { LessOrEqual(t, 2, 1, msgAndArgs...) }, 486 | func(t TestingT) { Positive(t, 0, msgAndArgs...) }, 487 | func(t TestingT) { Negative(t, 0, msgAndArgs...) }, 488 | } 489 | for _, f := range funcs { 490 | out := &outputT{buf: bytes.NewBuffer(nil)} 491 | f(out) 492 | Contains(t, out.buf.String(), expectedOutput) 493 | } 494 | } 495 | -------------------------------------------------------------------------------- /assert/assertion_format.go.tmpl: -------------------------------------------------------------------------------- 1 | {{.CommentFormat}} 2 | func {{.DocInfo.Name}}f(t TestingT, {{.ParamsFormat}}) bool { 3 | if h, ok := t.(tHelper); ok { h.Helper() } 4 | return {{.DocInfo.Name}}(t, {{.ForwardedParamsFormat}}) 5 | } 6 | -------------------------------------------------------------------------------- /assert/assertion_forward.go.tmpl: -------------------------------------------------------------------------------- 1 | {{.CommentWithoutT "a"}} 2 | func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) bool { 3 | if h, ok := a.t.(tHelper); ok { h.Helper() } 4 | return {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) 5 | } 6 | -------------------------------------------------------------------------------- /assert/assertion_order.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | // isOrdered checks that collection contains orderable elements. 9 | func isOrdered(t TestingT, object interface{}, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...interface{}) bool { 10 | objKind := reflect.TypeOf(object).Kind() 11 | if objKind != reflect.Slice && objKind != reflect.Array { 12 | return false 13 | } 14 | 15 | objValue := reflect.ValueOf(object) 16 | objLen := objValue.Len() 17 | 18 | if objLen <= 1 { 19 | return true 20 | } 21 | 22 | value := objValue.Index(0) 23 | valueInterface := value.Interface() 24 | firstValueKind := value.Kind() 25 | 26 | for i := 1; i < objLen; i++ { 27 | prevValue := value 28 | prevValueInterface := valueInterface 29 | 30 | value = objValue.Index(i) 31 | valueInterface = value.Interface() 32 | 33 | compareResult, isComparable := compare(prevValueInterface, valueInterface, firstValueKind) 34 | 35 | if !isComparable { 36 | return Fail(t, fmt.Sprintf(`Can not compare type "%T" and "%T"`, value, prevValue), msgAndArgs...) 37 | } 38 | 39 | if !containsValue(allowedComparesResults, compareResult) { 40 | return Fail(t, fmt.Sprintf(failMessage, prevValue, value), msgAndArgs...) 41 | } 42 | } 43 | 44 | return true 45 | } 46 | 47 | // IsIncreasing asserts that the collection is increasing 48 | // 49 | // assert.IsIncreasing(t, []int{1, 2, 3}) 50 | // assert.IsIncreasing(t, []float{1, 2}) 51 | // assert.IsIncreasing(t, []string{"a", "b"}) 52 | func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { 53 | return isOrdered(t, object, []compareResult{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) 54 | } 55 | 56 | // IsNonIncreasing asserts that the collection is not increasing 57 | // 58 | // assert.IsNonIncreasing(t, []int{2, 1, 1}) 59 | // assert.IsNonIncreasing(t, []float{2, 1}) 60 | // assert.IsNonIncreasing(t, []string{"b", "a"}) 61 | func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { 62 | return isOrdered(t, object, []compareResult{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) 63 | } 64 | 65 | // IsDecreasing asserts that the collection is decreasing 66 | // 67 | // assert.IsDecreasing(t, []int{2, 1, 0}) 68 | // assert.IsDecreasing(t, []float{2, 1}) 69 | // assert.IsDecreasing(t, []string{"b", "a"}) 70 | func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { 71 | return isOrdered(t, object, []compareResult{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) 72 | } 73 | 74 | // IsNonDecreasing asserts that the collection is not decreasing 75 | // 76 | // assert.IsNonDecreasing(t, []int{1, 1, 2}) 77 | // assert.IsNonDecreasing(t, []float{1, 2}) 78 | // assert.IsNonDecreasing(t, []string{"a", "b"}) 79 | func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { 80 | return isOrdered(t, object, []compareResult{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) 81 | } 82 | -------------------------------------------------------------------------------- /assert/assertion_order_test.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | func TestIsIncreasing(t *testing.T) { 9 | t.Parallel() 10 | 11 | mockT := new(testing.T) 12 | 13 | if !IsIncreasing(mockT, []int{1, 2}) { 14 | t.Error("IsIncreasing should return true") 15 | } 16 | 17 | if !IsIncreasing(mockT, []int{1, 2, 3, 4, 5}) { 18 | t.Error("IsIncreasing should return true") 19 | } 20 | 21 | if IsIncreasing(mockT, []int{1, 1}) { 22 | t.Error("IsIncreasing should return false") 23 | } 24 | 25 | if IsIncreasing(mockT, []int{2, 1}) { 26 | t.Error("IsIncreasing should return false") 27 | } 28 | 29 | // Check error report 30 | for _, currCase := range []struct { 31 | collection interface{} 32 | msg string 33 | }{ 34 | {collection: []string{"b", "a"}, msg: `"b" is not less than "a"`}, 35 | {collection: []int{2, 1}, msg: `"2" is not less than "1"`}, 36 | {collection: []int{2, 1, 3, 4, 5, 6, 7}, msg: `"2" is not less than "1"`}, 37 | {collection: []int{-1, 0, 2, 1}, msg: `"2" is not less than "1"`}, 38 | {collection: []int8{2, 1}, msg: `"2" is not less than "1"`}, 39 | {collection: []int16{2, 1}, msg: `"2" is not less than "1"`}, 40 | {collection: []int32{2, 1}, msg: `"2" is not less than "1"`}, 41 | {collection: []int64{2, 1}, msg: `"2" is not less than "1"`}, 42 | {collection: []uint8{2, 1}, msg: `"2" is not less than "1"`}, 43 | {collection: []uint16{2, 1}, msg: `"2" is not less than "1"`}, 44 | {collection: []uint32{2, 1}, msg: `"2" is not less than "1"`}, 45 | {collection: []uint64{2, 1}, msg: `"2" is not less than "1"`}, 46 | {collection: []float32{2.34, 1.23}, msg: `"2.34" is not less than "1.23"`}, 47 | {collection: []float64{2.34, 1.23}, msg: `"2.34" is not less than "1.23"`}, 48 | } { 49 | out := &outputT{buf: bytes.NewBuffer(nil)} 50 | False(t, IsIncreasing(out, currCase.collection)) 51 | Contains(t, out.buf.String(), currCase.msg) 52 | } 53 | } 54 | 55 | func TestIsNonIncreasing(t *testing.T) { 56 | t.Parallel() 57 | 58 | mockT := new(testing.T) 59 | 60 | if !IsNonIncreasing(mockT, []int{2, 1}) { 61 | t.Error("IsNonIncreasing should return true") 62 | } 63 | 64 | if !IsNonIncreasing(mockT, []int{5, 4, 4, 3, 2, 1}) { 65 | t.Error("IsNonIncreasing should return true") 66 | } 67 | 68 | if !IsNonIncreasing(mockT, []int{1, 1}) { 69 | t.Error("IsNonIncreasing should return true") 70 | } 71 | 72 | if IsNonIncreasing(mockT, []int{1, 2}) { 73 | t.Error("IsNonIncreasing should return false") 74 | } 75 | 76 | // Check error report 77 | for _, currCase := range []struct { 78 | collection interface{} 79 | msg string 80 | }{ 81 | {collection: []string{"a", "b"}, msg: `"a" is not greater than or equal to "b"`}, 82 | {collection: []int{1, 2}, msg: `"1" is not greater than or equal to "2"`}, 83 | {collection: []int{1, 2, 7, 6, 5, 4, 3}, msg: `"1" is not greater than or equal to "2"`}, 84 | {collection: []int{5, 4, 3, 1, 2}, msg: `"1" is not greater than or equal to "2"`}, 85 | {collection: []int8{1, 2}, msg: `"1" is not greater than or equal to "2"`}, 86 | {collection: []int16{1, 2}, msg: `"1" is not greater than or equal to "2"`}, 87 | {collection: []int32{1, 2}, msg: `"1" is not greater than or equal to "2"`}, 88 | {collection: []int64{1, 2}, msg: `"1" is not greater than or equal to "2"`}, 89 | {collection: []uint8{1, 2}, msg: `"1" is not greater than or equal to "2"`}, 90 | {collection: []uint16{1, 2}, msg: `"1" is not greater than or equal to "2"`}, 91 | {collection: []uint32{1, 2}, msg: `"1" is not greater than or equal to "2"`}, 92 | {collection: []uint64{1, 2}, msg: `"1" is not greater than or equal to "2"`}, 93 | {collection: []float32{1.23, 2.34}, msg: `"1.23" is not greater than or equal to "2.34"`}, 94 | {collection: []float64{1.23, 2.34}, msg: `"1.23" is not greater than or equal to "2.34"`}, 95 | } { 96 | out := &outputT{buf: bytes.NewBuffer(nil)} 97 | False(t, IsNonIncreasing(out, currCase.collection)) 98 | Contains(t, out.buf.String(), currCase.msg) 99 | } 100 | } 101 | 102 | func TestIsDecreasing(t *testing.T) { 103 | t.Parallel() 104 | 105 | mockT := new(testing.T) 106 | 107 | if !IsDecreasing(mockT, []int{2, 1}) { 108 | t.Error("IsDecreasing should return true") 109 | } 110 | 111 | if !IsDecreasing(mockT, []int{5, 4, 3, 2, 1}) { 112 | t.Error("IsDecreasing should return true") 113 | } 114 | 115 | if IsDecreasing(mockT, []int{1, 1}) { 116 | t.Error("IsDecreasing should return false") 117 | } 118 | 119 | if IsDecreasing(mockT, []int{1, 2}) { 120 | t.Error("IsDecreasing should return false") 121 | } 122 | 123 | // Check error report 124 | for _, currCase := range []struct { 125 | collection interface{} 126 | msg string 127 | }{ 128 | {collection: []string{"a", "b"}, msg: `"a" is not greater than "b"`}, 129 | {collection: []int{1, 2}, msg: `"1" is not greater than "2"`}, 130 | {collection: []int{1, 2, 7, 6, 5, 4, 3}, msg: `"1" is not greater than "2"`}, 131 | {collection: []int{5, 4, 3, 1, 2}, msg: `"1" is not greater than "2"`}, 132 | {collection: []int8{1, 2}, msg: `"1" is not greater than "2"`}, 133 | {collection: []int16{1, 2}, msg: `"1" is not greater than "2"`}, 134 | {collection: []int32{1, 2}, msg: `"1" is not greater than "2"`}, 135 | {collection: []int64{1, 2}, msg: `"1" is not greater than "2"`}, 136 | {collection: []uint8{1, 2}, msg: `"1" is not greater than "2"`}, 137 | {collection: []uint16{1, 2}, msg: `"1" is not greater than "2"`}, 138 | {collection: []uint32{1, 2}, msg: `"1" is not greater than "2"`}, 139 | {collection: []uint64{1, 2}, msg: `"1" is not greater than "2"`}, 140 | {collection: []float32{1.23, 2.34}, msg: `"1.23" is not greater than "2.34"`}, 141 | {collection: []float64{1.23, 2.34}, msg: `"1.23" is not greater than "2.34"`}, 142 | } { 143 | out := &outputT{buf: bytes.NewBuffer(nil)} 144 | False(t, IsDecreasing(out, currCase.collection)) 145 | Contains(t, out.buf.String(), currCase.msg) 146 | } 147 | } 148 | 149 | func TestIsNonDecreasing(t *testing.T) { 150 | t.Parallel() 151 | 152 | mockT := new(testing.T) 153 | 154 | if !IsNonDecreasing(mockT, []int{1, 2}) { 155 | t.Error("IsNonDecreasing should return true") 156 | } 157 | 158 | if !IsNonDecreasing(mockT, []int{1, 1, 2, 3, 4, 5}) { 159 | t.Error("IsNonDecreasing should return true") 160 | } 161 | 162 | if !IsNonDecreasing(mockT, []int{1, 1}) { 163 | t.Error("IsNonDecreasing should return false") 164 | } 165 | 166 | if IsNonDecreasing(mockT, []int{2, 1}) { 167 | t.Error("IsNonDecreasing should return false") 168 | } 169 | 170 | // Check error report 171 | for _, currCase := range []struct { 172 | collection interface{} 173 | msg string 174 | }{ 175 | {collection: []string{"b", "a"}, msg: `"b" is not less than or equal to "a"`}, 176 | {collection: []int{2, 1}, msg: `"2" is not less than or equal to "1"`}, 177 | {collection: []int{2, 1, 3, 4, 5, 6, 7}, msg: `"2" is not less than or equal to "1"`}, 178 | {collection: []int{-1, 0, 2, 1}, msg: `"2" is not less than or equal to "1"`}, 179 | {collection: []int8{2, 1}, msg: `"2" is not less than or equal to "1"`}, 180 | {collection: []int16{2, 1}, msg: `"2" is not less than or equal to "1"`}, 181 | {collection: []int32{2, 1}, msg: `"2" is not less than or equal to "1"`}, 182 | {collection: []int64{2, 1}, msg: `"2" is not less than or equal to "1"`}, 183 | {collection: []uint8{2, 1}, msg: `"2" is not less than or equal to "1"`}, 184 | {collection: []uint16{2, 1}, msg: `"2" is not less than or equal to "1"`}, 185 | {collection: []uint32{2, 1}, msg: `"2" is not less than or equal to "1"`}, 186 | {collection: []uint64{2, 1}, msg: `"2" is not less than or equal to "1"`}, 187 | {collection: []float32{2.34, 1.23}, msg: `"2.34" is not less than or equal to "1.23"`}, 188 | {collection: []float64{2.34, 1.23}, msg: `"2.34" is not less than or equal to "1.23"`}, 189 | } { 190 | out := &outputT{buf: bytes.NewBuffer(nil)} 191 | False(t, IsNonDecreasing(out, currCase.collection)) 192 | Contains(t, out.buf.String(), currCase.msg) 193 | } 194 | } 195 | 196 | func TestOrderingMsgAndArgsForwarding(t *testing.T) { 197 | t.Parallel() 198 | 199 | msgAndArgs := []interface{}{"format %s %x", "this", 0xc001} 200 | expectedOutput := "format this c001\n" 201 | collection := []int{1, 2, 1} 202 | funcs := []func(t TestingT){ 203 | func(t TestingT) { IsIncreasing(t, collection, msgAndArgs...) }, 204 | func(t TestingT) { IsNonIncreasing(t, collection, msgAndArgs...) }, 205 | func(t TestingT) { IsDecreasing(t, collection, msgAndArgs...) }, 206 | func(t TestingT) { IsNonDecreasing(t, collection, msgAndArgs...) }, 207 | } 208 | for _, f := range funcs { 209 | out := &outputT{buf: bytes.NewBuffer(nil)} 210 | f(out) 211 | Contains(t, out.buf.String(), expectedOutput) 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /assert/doc.go: -------------------------------------------------------------------------------- 1 | // Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. 2 | // 3 | // # Note 4 | // 5 | // All functions in this package return a bool value indicating whether the assertion has passed. 6 | // 7 | // # Example Usage 8 | // 9 | // The following is a complete example using assert in a standard test function: 10 | // 11 | // import ( 12 | // "testing" 13 | // "github.com/stretchr/testify/assert" 14 | // ) 15 | // 16 | // func TestSomething(t *testing.T) { 17 | // 18 | // var a string = "Hello" 19 | // var b string = "Hello" 20 | // 21 | // assert.Equal(t, a, b, "The two words should be the same.") 22 | // 23 | // } 24 | // 25 | // if you assert many times, use the format below: 26 | // 27 | // import ( 28 | // "testing" 29 | // "github.com/stretchr/testify/assert" 30 | // ) 31 | // 32 | // func TestSomething(t *testing.T) { 33 | // assert := assert.New(t) 34 | // 35 | // var a string = "Hello" 36 | // var b string = "Hello" 37 | // 38 | // assert.Equal(a, b, "The two words should be the same.") 39 | // } 40 | // 41 | // # Assertions 42 | // 43 | // Assertions allow you to easily write test code, and are global funcs in the `assert` package. 44 | // All assertion functions take, as the first argument, the `*testing.T` object provided by the 45 | // testing framework. This allows the assertion funcs to write the failings and other details to 46 | // the correct place. 47 | // 48 | // Every assertion function also takes an optional string message as the final argument, 49 | // allowing custom error messages to be appended to the message the assertion method outputs. 50 | package assert 51 | -------------------------------------------------------------------------------- /assert/errors.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // AnError is an error instance useful for testing. If the code does not care 8 | // about error specifics, and only needs to return the error for example, this 9 | // error should be used to make the test code more readable. 10 | var AnError = errors.New("assert.AnError general error for testing") 11 | -------------------------------------------------------------------------------- /assert/forward_assertions.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | // Assertions provides assertion methods around the 4 | // TestingT interface. 5 | type Assertions struct { 6 | t TestingT 7 | } 8 | 9 | // New makes a new Assertions object for the specified TestingT. 10 | func New(t TestingT) *Assertions { 11 | return &Assertions{ 12 | t: t, 13 | } 14 | } 15 | 16 | //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs" 17 | -------------------------------------------------------------------------------- /assert/forward_assertions_test.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "errors" 5 | "regexp" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestImplementsWrapper(t *testing.T) { 11 | t.Parallel() 12 | 13 | assert := New(new(testing.T)) 14 | 15 | if !assert.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject)) { 16 | t.Error("Implements method should return true: AssertionTesterConformingObject implements AssertionTesterInterface") 17 | } 18 | if assert.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterNonConformingObject)) { 19 | t.Error("Implements method should return false: AssertionTesterNonConformingObject does not implements AssertionTesterInterface") 20 | } 21 | } 22 | 23 | func TestIsTypeWrapper(t *testing.T) { 24 | t.Parallel() 25 | 26 | assert := New(new(testing.T)) 27 | 28 | if !assert.IsType(new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) { 29 | t.Error("IsType should return true: AssertionTesterConformingObject is the same type as AssertionTesterConformingObject") 30 | } 31 | if assert.IsType(new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject)) { 32 | t.Error("IsType should return false: AssertionTesterConformingObject is not the same type as AssertionTesterNonConformingObject") 33 | } 34 | 35 | } 36 | 37 | func TestEqualWrapper(t *testing.T) { 38 | t.Parallel() 39 | 40 | assert := New(new(testing.T)) 41 | 42 | if !assert.Equal("Hello World", "Hello World") { 43 | t.Error("Equal should return true") 44 | } 45 | if !assert.Equal(123, 123) { 46 | t.Error("Equal should return true") 47 | } 48 | if !assert.Equal(123.5, 123.5) { 49 | t.Error("Equal should return true") 50 | } 51 | if !assert.Equal([]byte("Hello World"), []byte("Hello World")) { 52 | t.Error("Equal should return true") 53 | } 54 | if !assert.Equal(nil, nil) { 55 | t.Error("Equal should return true") 56 | } 57 | } 58 | 59 | func TestEqualValuesWrapper(t *testing.T) { 60 | t.Parallel() 61 | 62 | assert := New(new(testing.T)) 63 | 64 | if !assert.EqualValues(uint32(10), int32(10)) { 65 | t.Error("EqualValues should return true") 66 | } 67 | } 68 | 69 | func TestNotNilWrapper(t *testing.T) { 70 | t.Parallel() 71 | 72 | assert := New(new(testing.T)) 73 | 74 | if !assert.NotNil(new(AssertionTesterConformingObject)) { 75 | t.Error("NotNil should return true: object is not nil") 76 | } 77 | if assert.NotNil(nil) { 78 | t.Error("NotNil should return false: object is nil") 79 | } 80 | 81 | } 82 | 83 | func TestNilWrapper(t *testing.T) { 84 | t.Parallel() 85 | 86 | assert := New(new(testing.T)) 87 | 88 | if !assert.Nil(nil) { 89 | t.Error("Nil should return true: object is nil") 90 | } 91 | if assert.Nil(new(AssertionTesterConformingObject)) { 92 | t.Error("Nil should return false: object is not nil") 93 | } 94 | 95 | } 96 | 97 | func TestTrueWrapper(t *testing.T) { 98 | t.Parallel() 99 | 100 | assert := New(new(testing.T)) 101 | 102 | if !assert.True(true) { 103 | t.Error("True should return true") 104 | } 105 | if assert.True(false) { 106 | t.Error("True should return false") 107 | } 108 | 109 | } 110 | 111 | func TestFalseWrapper(t *testing.T) { 112 | t.Parallel() 113 | 114 | assert := New(new(testing.T)) 115 | 116 | if !assert.False(false) { 117 | t.Error("False should return true") 118 | } 119 | if assert.False(true) { 120 | t.Error("False should return false") 121 | } 122 | 123 | } 124 | 125 | func TestExactlyWrapper(t *testing.T) { 126 | t.Parallel() 127 | 128 | assert := New(new(testing.T)) 129 | 130 | a := float32(1) 131 | b := float64(1) 132 | c := float32(1) 133 | d := float32(2) 134 | 135 | if assert.Exactly(a, b) { 136 | t.Error("Exactly should return false") 137 | } 138 | if assert.Exactly(a, d) { 139 | t.Error("Exactly should return false") 140 | } 141 | if !assert.Exactly(a, c) { 142 | t.Error("Exactly should return true") 143 | } 144 | 145 | if assert.Exactly(nil, a) { 146 | t.Error("Exactly should return false") 147 | } 148 | if assert.Exactly(a, nil) { 149 | t.Error("Exactly should return false") 150 | } 151 | 152 | } 153 | 154 | func TestNotEqualWrapper(t *testing.T) { 155 | t.Parallel() 156 | 157 | assert := New(new(testing.T)) 158 | 159 | if !assert.NotEqual("Hello World", "Hello World!") { 160 | t.Error("NotEqual should return true") 161 | } 162 | if !assert.NotEqual(123, 1234) { 163 | t.Error("NotEqual should return true") 164 | } 165 | if !assert.NotEqual(123.5, 123.55) { 166 | t.Error("NotEqual should return true") 167 | } 168 | if !assert.NotEqual([]byte("Hello World"), []byte("Hello World!")) { 169 | t.Error("NotEqual should return true") 170 | } 171 | if !assert.NotEqual(nil, new(AssertionTesterConformingObject)) { 172 | t.Error("NotEqual should return true") 173 | } 174 | } 175 | 176 | func TestNotEqualValuesWrapper(t *testing.T) { 177 | t.Parallel() 178 | 179 | assert := New(new(testing.T)) 180 | 181 | if !assert.NotEqualValues("Hello World", "Hello World!") { 182 | t.Error("NotEqualValues should return true") 183 | } 184 | if !assert.NotEqualValues(123, 1234) { 185 | t.Error("NotEqualValues should return true") 186 | } 187 | if !assert.NotEqualValues(123.5, 123.55) { 188 | t.Error("NotEqualValues should return true") 189 | } 190 | if !assert.NotEqualValues([]byte("Hello World"), []byte("Hello World!")) { 191 | t.Error("NotEqualValues should return true") 192 | } 193 | if !assert.NotEqualValues(nil, new(AssertionTesterConformingObject)) { 194 | t.Error("NotEqualValues should return true") 195 | } 196 | if assert.NotEqualValues(10, uint(10)) { 197 | t.Error("NotEqualValues should return false") 198 | } 199 | } 200 | 201 | func TestContainsWrapper(t *testing.T) { 202 | t.Parallel() 203 | 204 | assert := New(new(testing.T)) 205 | list := []string{"Foo", "Bar"} 206 | 207 | if !assert.Contains("Hello World", "Hello") { 208 | t.Error("Contains should return true: \"Hello World\" contains \"Hello\"") 209 | } 210 | if assert.Contains("Hello World", "Salut") { 211 | t.Error("Contains should return false: \"Hello World\" does not contain \"Salut\"") 212 | } 213 | 214 | if !assert.Contains(list, "Foo") { 215 | t.Error("Contains should return true: \"[\"Foo\", \"Bar\"]\" contains \"Foo\"") 216 | } 217 | if assert.Contains(list, "Salut") { 218 | t.Error("Contains should return false: \"[\"Foo\", \"Bar\"]\" does not contain \"Salut\"") 219 | } 220 | 221 | } 222 | 223 | func TestNotContainsWrapper(t *testing.T) { 224 | t.Parallel() 225 | 226 | assert := New(new(testing.T)) 227 | list := []string{"Foo", "Bar"} 228 | 229 | if !assert.NotContains("Hello World", "Hello!") { 230 | t.Error("NotContains should return true: \"Hello World\" does not contain \"Hello!\"") 231 | } 232 | if assert.NotContains("Hello World", "Hello") { 233 | t.Error("NotContains should return false: \"Hello World\" contains \"Hello\"") 234 | } 235 | 236 | if !assert.NotContains(list, "Foo!") { 237 | t.Error("NotContains should return true: \"[\"Foo\", \"Bar\"]\" does not contain \"Foo!\"") 238 | } 239 | if assert.NotContains(list, "Foo") { 240 | t.Error("NotContains should return false: \"[\"Foo\", \"Bar\"]\" contains \"Foo\"") 241 | } 242 | 243 | } 244 | 245 | func TestConditionWrapper(t *testing.T) { 246 | t.Parallel() 247 | 248 | assert := New(new(testing.T)) 249 | 250 | if !assert.Condition(func() bool { return true }, "Truth") { 251 | t.Error("Condition should return true") 252 | } 253 | 254 | if assert.Condition(func() bool { return false }, "Lie") { 255 | t.Error("Condition should return false") 256 | } 257 | 258 | } 259 | 260 | func TestDidPanicWrapper(t *testing.T) { 261 | t.Parallel() 262 | 263 | if funcDidPanic, _, _ := didPanic(func() { 264 | panic("Panic!") 265 | }); !funcDidPanic { 266 | t.Error("didPanic should return true") 267 | } 268 | 269 | if funcDidPanic, _, _ := didPanic(func() { 270 | }); funcDidPanic { 271 | t.Error("didPanic should return false") 272 | } 273 | 274 | } 275 | 276 | func TestPanicsWrapper(t *testing.T) { 277 | t.Parallel() 278 | 279 | assert := New(new(testing.T)) 280 | 281 | if !assert.Panics(func() { 282 | panic("Panic!") 283 | }) { 284 | t.Error("Panics should return true") 285 | } 286 | 287 | if assert.Panics(func() { 288 | }) { 289 | t.Error("Panics should return false") 290 | } 291 | 292 | } 293 | 294 | func TestNotPanicsWrapper(t *testing.T) { 295 | t.Parallel() 296 | 297 | assert := New(new(testing.T)) 298 | 299 | if !assert.NotPanics(func() { 300 | }) { 301 | t.Error("NotPanics should return true") 302 | } 303 | 304 | if assert.NotPanics(func() { 305 | panic("Panic!") 306 | }) { 307 | t.Error("NotPanics should return false") 308 | } 309 | 310 | } 311 | 312 | func TestNoErrorWrapper(t *testing.T) { 313 | t.Parallel() 314 | 315 | assert := New(t) 316 | mockAssert := New(new(testing.T)) 317 | 318 | // start with a nil error 319 | var err error 320 | 321 | assert.True(mockAssert.NoError(err), "NoError should return True for nil arg") 322 | 323 | // now set an error 324 | err = errors.New("Some error") 325 | 326 | assert.False(mockAssert.NoError(err), "NoError with error should return False") 327 | 328 | } 329 | 330 | func TestErrorWrapper(t *testing.T) { 331 | t.Parallel() 332 | 333 | assert := New(t) 334 | mockAssert := New(new(testing.T)) 335 | 336 | // start with a nil error 337 | var err error 338 | 339 | assert.False(mockAssert.Error(err), "Error should return False for nil arg") 340 | 341 | // now set an error 342 | err = errors.New("Some error") 343 | 344 | assert.True(mockAssert.Error(err), "Error with error should return True") 345 | 346 | } 347 | 348 | func TestErrorContainsWrapper(t *testing.T) { 349 | t.Parallel() 350 | 351 | assert := New(t) 352 | mockAssert := New(new(testing.T)) 353 | 354 | // start with a nil error 355 | var err error 356 | assert.False(mockAssert.ErrorContains(err, ""), 357 | "ErrorContains should return false for nil arg") 358 | 359 | // now set an error 360 | err = errors.New("some error: another error") 361 | assert.False(mockAssert.ErrorContains(err, "different error"), 362 | "ErrorContains should return false for different error string") 363 | assert.True(mockAssert.ErrorContains(err, "some error"), 364 | "ErrorContains should return true") 365 | assert.True(mockAssert.ErrorContains(err, "another error"), 366 | "ErrorContains should return true") 367 | } 368 | 369 | func TestEqualErrorWrapper(t *testing.T) { 370 | t.Parallel() 371 | 372 | assert := New(t) 373 | mockAssert := New(new(testing.T)) 374 | 375 | // start with a nil error 376 | var err error 377 | assert.False(mockAssert.EqualError(err, ""), 378 | "EqualError should return false for nil arg") 379 | 380 | // now set an error 381 | err = errors.New("some error") 382 | assert.False(mockAssert.EqualError(err, "Not some error"), 383 | "EqualError should return false for different error string") 384 | assert.True(mockAssert.EqualError(err, "some error"), 385 | "EqualError should return true") 386 | } 387 | 388 | func TestEmptyWrapper(t *testing.T) { 389 | t.Parallel() 390 | 391 | assert := New(t) 392 | mockAssert := New(new(testing.T)) 393 | 394 | assert.True(mockAssert.Empty(""), "Empty string is empty") 395 | assert.True(mockAssert.Empty(nil), "Nil is empty") 396 | assert.True(mockAssert.Empty([]string{}), "Empty string array is empty") 397 | assert.True(mockAssert.Empty(0), "Zero int value is empty") 398 | assert.True(mockAssert.Empty(false), "False value is empty") 399 | 400 | assert.False(mockAssert.Empty("something"), "Non Empty string is not empty") 401 | assert.False(mockAssert.Empty(errors.New("something")), "Non nil object is not empty") 402 | assert.False(mockAssert.Empty([]string{"something"}), "Non empty string array is not empty") 403 | assert.False(mockAssert.Empty(1), "Non-zero int value is not empty") 404 | assert.False(mockAssert.Empty(true), "True value is not empty") 405 | 406 | } 407 | 408 | func TestNotEmptyWrapper(t *testing.T) { 409 | t.Parallel() 410 | 411 | assert := New(t) 412 | mockAssert := New(new(testing.T)) 413 | 414 | assert.False(mockAssert.NotEmpty(""), "Empty string is empty") 415 | assert.False(mockAssert.NotEmpty(nil), "Nil is empty") 416 | assert.False(mockAssert.NotEmpty([]string{}), "Empty string array is empty") 417 | assert.False(mockAssert.NotEmpty(0), "Zero int value is empty") 418 | assert.False(mockAssert.NotEmpty(false), "False value is empty") 419 | 420 | assert.True(mockAssert.NotEmpty("something"), "Non Empty string is not empty") 421 | assert.True(mockAssert.NotEmpty(errors.New("something")), "Non nil object is not empty") 422 | assert.True(mockAssert.NotEmpty([]string{"something"}), "Non empty string array is not empty") 423 | assert.True(mockAssert.NotEmpty(1), "Non-zero int value is not empty") 424 | assert.True(mockAssert.NotEmpty(true), "True value is not empty") 425 | 426 | } 427 | 428 | func TestLenWrapper(t *testing.T) { 429 | t.Parallel() 430 | 431 | assert := New(t) 432 | mockAssert := New(new(testing.T)) 433 | 434 | assert.False(mockAssert.Len(nil, 0), "nil does not have length") 435 | assert.False(mockAssert.Len(0, 0), "int does not have length") 436 | assert.False(mockAssert.Len(true, 0), "true does not have length") 437 | assert.False(mockAssert.Len(false, 0), "false does not have length") 438 | assert.False(mockAssert.Len('A', 0), "Rune does not have length") 439 | assert.False(mockAssert.Len(struct{}{}, 0), "Struct does not have length") 440 | 441 | ch := make(chan int, 5) 442 | ch <- 1 443 | ch <- 2 444 | ch <- 3 445 | 446 | cases := []struct { 447 | v interface{} 448 | l int 449 | }{ 450 | {[]int{1, 2, 3}, 3}, 451 | {[...]int{1, 2, 3}, 3}, 452 | {"ABC", 3}, 453 | {map[int]int{1: 2, 2: 4, 3: 6}, 3}, 454 | {ch, 3}, 455 | 456 | {[]int{}, 0}, 457 | {map[int]int{}, 0}, 458 | {make(chan int), 0}, 459 | 460 | {[]int(nil), 0}, 461 | {map[int]int(nil), 0}, 462 | {(chan int)(nil), 0}, 463 | } 464 | 465 | for _, c := range cases { 466 | assert.True(mockAssert.Len(c.v, c.l), "%#v have %d items", c.v, c.l) 467 | } 468 | } 469 | 470 | func TestWithinDurationWrapper(t *testing.T) { 471 | t.Parallel() 472 | 473 | assert := New(t) 474 | mockAssert := New(new(testing.T)) 475 | a := time.Now() 476 | b := a.Add(10 * time.Second) 477 | 478 | assert.True(mockAssert.WithinDuration(a, b, 10*time.Second), "A 10s difference is within a 10s time difference") 479 | assert.True(mockAssert.WithinDuration(b, a, 10*time.Second), "A 10s difference is within a 10s time difference") 480 | 481 | assert.False(mockAssert.WithinDuration(a, b, 9*time.Second), "A 10s difference is not within a 9s time difference") 482 | assert.False(mockAssert.WithinDuration(b, a, 9*time.Second), "A 10s difference is not within a 9s time difference") 483 | 484 | assert.False(mockAssert.WithinDuration(a, b, -9*time.Second), "A 10s difference is not within a 9s time difference") 485 | assert.False(mockAssert.WithinDuration(b, a, -9*time.Second), "A 10s difference is not within a 9s time difference") 486 | 487 | assert.False(mockAssert.WithinDuration(a, b, -11*time.Second), "A 10s difference is not within a 9s time difference") 488 | assert.False(mockAssert.WithinDuration(b, a, -11*time.Second), "A 10s difference is not within a 9s time difference") 489 | } 490 | 491 | func TestInDeltaWrapper(t *testing.T) { 492 | t.Parallel() 493 | 494 | assert := New(new(testing.T)) 495 | 496 | True(t, assert.InDelta(1.001, 1, 0.01), "|1.001 - 1| <= 0.01") 497 | True(t, assert.InDelta(1, 1.001, 0.01), "|1 - 1.001| <= 0.01") 498 | True(t, assert.InDelta(1, 2, 1), "|1 - 2| <= 1") 499 | False(t, assert.InDelta(1, 2, 0.5), "Expected |1 - 2| <= 0.5 to fail") 500 | False(t, assert.InDelta(2, 1, 0.5), "Expected |2 - 1| <= 0.5 to fail") 501 | False(t, assert.InDelta("", nil, 1), "Expected non numerals to fail") 502 | 503 | cases := []struct { 504 | a, b interface{} 505 | delta float64 506 | }{ 507 | {uint8(2), uint8(1), 1}, 508 | {uint16(2), uint16(1), 1}, 509 | {uint32(2), uint32(1), 1}, 510 | {uint64(2), uint64(1), 1}, 511 | 512 | {int(2), int(1), 1}, 513 | {int8(2), int8(1), 1}, 514 | {int16(2), int16(1), 1}, 515 | {int32(2), int32(1), 1}, 516 | {int64(2), int64(1), 1}, 517 | 518 | {float32(2), float32(1), 1}, 519 | {float64(2), float64(1), 1}, 520 | } 521 | 522 | for _, tc := range cases { 523 | True(t, assert.InDelta(tc.a, tc.b, tc.delta), "Expected |%V - %V| <= %v", tc.a, tc.b, tc.delta) 524 | } 525 | } 526 | 527 | func TestInEpsilonWrapper(t *testing.T) { 528 | t.Parallel() 529 | 530 | assert := New(new(testing.T)) 531 | 532 | cases := []struct { 533 | a, b interface{} 534 | epsilon float64 535 | }{ 536 | {uint8(2), uint16(2), .001}, 537 | {2.1, 2.2, 0.1}, 538 | {2.2, 2.1, 0.1}, 539 | {-2.1, -2.2, 0.1}, 540 | {-2.2, -2.1, 0.1}, 541 | {uint64(100), uint8(101), 0.01}, 542 | {0.1, -0.1, 2}, 543 | } 544 | 545 | for _, tc := range cases { 546 | True(t, assert.InEpsilon(tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon)) 547 | } 548 | 549 | cases = []struct { 550 | a, b interface{} 551 | epsilon float64 552 | }{ 553 | {uint8(2), int16(-2), .001}, 554 | {uint64(100), uint8(102), 0.01}, 555 | {2.1, 2.2, 0.001}, 556 | {2.2, 2.1, 0.001}, 557 | {2.1, -2.2, 1}, 558 | {2.1, "bla-bla", 0}, 559 | {0.1, -0.1, 1.99}, 560 | } 561 | 562 | for _, tc := range cases { 563 | False(t, assert.InEpsilon(tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon)) 564 | } 565 | } 566 | 567 | func TestRegexpWrapper(t *testing.T) { 568 | t.Parallel() 569 | 570 | assert := New(new(testing.T)) 571 | 572 | cases := []struct { 573 | rx, str string 574 | }{ 575 | {"^start", "start of the line"}, 576 | {"end$", "in the end"}, 577 | {"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12.34"}, 578 | } 579 | 580 | for _, tc := range cases { 581 | True(t, assert.Regexp(tc.rx, tc.str)) 582 | True(t, assert.Regexp(regexp.MustCompile(tc.rx), tc.str)) 583 | False(t, assert.NotRegexp(tc.rx, tc.str)) 584 | False(t, assert.NotRegexp(regexp.MustCompile(tc.rx), tc.str)) 585 | } 586 | 587 | cases = []struct { 588 | rx, str string 589 | }{ 590 | {"^asdfastart", "Not the start of the line"}, 591 | {"end$", "in the end."}, 592 | {"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12a.34"}, 593 | } 594 | 595 | for _, tc := range cases { 596 | False(t, assert.Regexp(tc.rx, tc.str), "Expected %q to not match %q", tc.rx, tc.str) 597 | False(t, assert.Regexp(regexp.MustCompile(tc.rx), tc.str)) 598 | True(t, assert.NotRegexp(tc.rx, tc.str)) 599 | True(t, assert.NotRegexp(regexp.MustCompile(tc.rx), tc.str)) 600 | } 601 | } 602 | 603 | func TestZeroWrapper(t *testing.T) { 604 | t.Parallel() 605 | 606 | assert := New(t) 607 | mockAssert := New(new(testing.T)) 608 | 609 | for _, test := range zeros { 610 | assert.True(mockAssert.Zero(test), "Zero should return true for %v", test) 611 | } 612 | 613 | for _, test := range nonZeros { 614 | assert.False(mockAssert.Zero(test), "Zero should return false for %v", test) 615 | } 616 | } 617 | 618 | func TestNotZeroWrapper(t *testing.T) { 619 | t.Parallel() 620 | 621 | assert := New(t) 622 | mockAssert := New(new(testing.T)) 623 | 624 | for _, test := range zeros { 625 | assert.False(mockAssert.NotZero(test), "Zero should return true for %v", test) 626 | } 627 | 628 | for _, test := range nonZeros { 629 | assert.True(mockAssert.NotZero(test), "Zero should return false for %v", test) 630 | } 631 | } 632 | 633 | func TestJSONEqWrapper_EqualSONString(t *testing.T) { 634 | t.Parallel() 635 | 636 | assert := New(new(testing.T)) 637 | if !assert.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) { 638 | t.Error("JSONEq should return true") 639 | } 640 | 641 | } 642 | 643 | func TestJSONEqWrapper_EquivalentButNotEqual(t *testing.T) { 644 | t.Parallel() 645 | 646 | assert := New(new(testing.T)) 647 | if !assert.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) { 648 | t.Error("JSONEq should return true") 649 | } 650 | 651 | } 652 | 653 | func TestJSONEqWrapper_HashOfArraysAndHashes(t *testing.T) { 654 | t.Parallel() 655 | 656 | assert := New(new(testing.T)) 657 | 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}", 658 | "{\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}") { 659 | t.Error("JSONEq should return true") 660 | } 661 | } 662 | 663 | func TestJSONEqWrapper_Array(t *testing.T) { 664 | t.Parallel() 665 | 666 | assert := New(new(testing.T)) 667 | if !assert.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) { 668 | t.Error("JSONEq should return true") 669 | } 670 | 671 | } 672 | 673 | func TestJSONEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { 674 | t.Parallel() 675 | 676 | assert := New(new(testing.T)) 677 | if assert.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) { 678 | t.Error("JSONEq should return false") 679 | } 680 | } 681 | 682 | func TestJSONEqWrapper_HashesNotEquivalent(t *testing.T) { 683 | t.Parallel() 684 | 685 | assert := New(new(testing.T)) 686 | if assert.JSONEq(`{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) { 687 | t.Error("JSONEq should return false") 688 | } 689 | } 690 | 691 | func TestJSONEqWrapper_ActualIsNotJSON(t *testing.T) { 692 | t.Parallel() 693 | 694 | assert := New(new(testing.T)) 695 | if assert.JSONEq(`{"foo": "bar"}`, "Not JSON") { 696 | t.Error("JSONEq should return false") 697 | } 698 | } 699 | 700 | func TestJSONEqWrapper_ExpectedIsNotJSON(t *testing.T) { 701 | t.Parallel() 702 | 703 | assert := New(new(testing.T)) 704 | if assert.JSONEq("Not JSON", `{"foo": "bar", "hello": "world"}`) { 705 | t.Error("JSONEq should return false") 706 | } 707 | } 708 | 709 | func TestJSONEqWrapper_ExpectedAndActualNotJSON(t *testing.T) { 710 | t.Parallel() 711 | 712 | assert := New(new(testing.T)) 713 | if assert.JSONEq("Not JSON", "Not JSON") { 714 | t.Error("JSONEq should return false") 715 | } 716 | } 717 | 718 | func TestJSONEqWrapper_ArraysOfDifferentOrder(t *testing.T) { 719 | t.Parallel() 720 | 721 | assert := New(new(testing.T)) 722 | if assert.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) { 723 | t.Error("JSONEq should return false") 724 | } 725 | } 726 | 727 | func TestYAMLEqWrapper_EqualYAMLString(t *testing.T) { 728 | t.Parallel() 729 | 730 | assert := New(new(testing.T)) 731 | if !assert.YAMLEq(`{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) { 732 | t.Error("YAMLEq should return true") 733 | } 734 | 735 | } 736 | 737 | func TestYAMLEqWrapper_EquivalentButNotEqual(t *testing.T) { 738 | t.Parallel() 739 | 740 | assert := New(new(testing.T)) 741 | if !assert.YAMLEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) { 742 | t.Error("YAMLEq should return true") 743 | } 744 | 745 | } 746 | 747 | func TestYAMLEqWrapper_HashOfArraysAndHashes(t *testing.T) { 748 | t.Parallel() 749 | 750 | assert := New(new(testing.T)) 751 | expected := ` 752 | numeric: 1.5 753 | array: 754 | - foo: bar 755 | - 1 756 | - "string" 757 | - ["nested", "array", 5.5] 758 | hash: 759 | nested: hash 760 | nested_slice: [this, is, nested] 761 | string: "foo" 762 | ` 763 | 764 | actual := ` 765 | numeric: 1.5 766 | hash: 767 | nested: hash 768 | nested_slice: [this, is, nested] 769 | string: "foo" 770 | array: 771 | - foo: bar 772 | - 1 773 | - "string" 774 | - ["nested", "array", 5.5] 775 | ` 776 | if !assert.YAMLEq(expected, actual) { 777 | t.Error("YAMLEq should return true") 778 | } 779 | } 780 | 781 | func TestYAMLEqWrapper_Array(t *testing.T) { 782 | t.Parallel() 783 | 784 | assert := New(new(testing.T)) 785 | if !assert.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) { 786 | t.Error("YAMLEq should return true") 787 | } 788 | 789 | } 790 | 791 | func TestYAMLEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { 792 | t.Parallel() 793 | 794 | assert := New(new(testing.T)) 795 | if assert.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) { 796 | t.Error("YAMLEq should return false") 797 | } 798 | } 799 | 800 | func TestYAMLEqWrapper_HashesNotEquivalent(t *testing.T) { 801 | t.Parallel() 802 | 803 | assert := New(new(testing.T)) 804 | if assert.YAMLEq(`{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) { 805 | t.Error("YAMLEq should return false") 806 | } 807 | } 808 | 809 | func TestYAMLEqWrapper_ActualIsSimpleString(t *testing.T) { 810 | t.Parallel() 811 | 812 | assert := New(new(testing.T)) 813 | if assert.YAMLEq(`{"foo": "bar"}`, "Simple String") { 814 | t.Error("YAMLEq should return false") 815 | } 816 | } 817 | 818 | func TestYAMLEqWrapper_ExpectedIsSimpleString(t *testing.T) { 819 | t.Parallel() 820 | 821 | assert := New(new(testing.T)) 822 | if assert.YAMLEq("Simple String", `{"foo": "bar", "hello": "world"}`) { 823 | t.Error("YAMLEq should return false") 824 | } 825 | } 826 | 827 | func TestYAMLEqWrapper_ExpectedAndActualSimpleString(t *testing.T) { 828 | t.Parallel() 829 | 830 | assert := New(new(testing.T)) 831 | if !assert.YAMLEq("Simple String", "Simple String") { 832 | t.Error("YAMLEq should return true") 833 | } 834 | } 835 | 836 | func TestYAMLEqWrapper_ArraysOfDifferentOrder(t *testing.T) { 837 | t.Parallel() 838 | 839 | assert := New(new(testing.T)) 840 | if assert.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) { 841 | t.Error("YAMLEq should return false") 842 | } 843 | } 844 | -------------------------------------------------------------------------------- /assert/http_assertions.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/http/httptest" 7 | "net/url" 8 | "strings" 9 | ) 10 | 11 | // httpCode is a helper that returns HTTP code of the response. It returns -1 and 12 | // an error if building a new request fails. 13 | func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (int, error) { 14 | w := httptest.NewRecorder() 15 | req, err := http.NewRequest(method, url, http.NoBody) 16 | if err != nil { 17 | return -1, err 18 | } 19 | req.URL.RawQuery = values.Encode() 20 | handler(w, req) 21 | return w.Code, nil 22 | } 23 | 24 | // HTTPSuccess asserts that a specified handler returns a success status code. 25 | // 26 | // assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) 27 | // 28 | // Returns whether the assertion was successful (true) or not (false). 29 | func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { 30 | if h, ok := t.(tHelper); ok { 31 | h.Helper() 32 | } 33 | code, err := httpCode(handler, method, url, values) 34 | if err != nil { 35 | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) 36 | } 37 | 38 | isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent 39 | if !isSuccessCode { 40 | Fail(t, fmt.Sprintf("Expected HTTP success status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...) 41 | } 42 | 43 | return isSuccessCode 44 | } 45 | 46 | // HTTPRedirect asserts that a specified handler returns a redirect status code. 47 | // 48 | // assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} 49 | // 50 | // Returns whether the assertion was successful (true) or not (false). 51 | func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { 52 | if h, ok := t.(tHelper); ok { 53 | h.Helper() 54 | } 55 | code, err := httpCode(handler, method, url, values) 56 | if err != nil { 57 | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) 58 | } 59 | 60 | isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect 61 | if !isRedirectCode { 62 | Fail(t, fmt.Sprintf("Expected HTTP redirect status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...) 63 | } 64 | 65 | return isRedirectCode 66 | } 67 | 68 | // HTTPError asserts that a specified handler returns an error status code. 69 | // 70 | // assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} 71 | // 72 | // Returns whether the assertion was successful (true) or not (false). 73 | func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { 74 | if h, ok := t.(tHelper); ok { 75 | h.Helper() 76 | } 77 | code, err := httpCode(handler, method, url, values) 78 | if err != nil { 79 | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) 80 | } 81 | 82 | isErrorCode := code >= http.StatusBadRequest 83 | if !isErrorCode { 84 | Fail(t, fmt.Sprintf("Expected HTTP error status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...) 85 | } 86 | 87 | return isErrorCode 88 | } 89 | 90 | // HTTPStatusCode asserts that a specified handler returns a specified status code. 91 | // 92 | // assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) 93 | // 94 | // Returns whether the assertion was successful (true) or not (false). 95 | func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { 96 | if h, ok := t.(tHelper); ok { 97 | h.Helper() 98 | } 99 | code, err := httpCode(handler, method, url, values) 100 | if err != nil { 101 | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) 102 | } 103 | 104 | successful := code == statuscode 105 | if !successful { 106 | Fail(t, fmt.Sprintf("Expected HTTP status code %d for %q but received %d", statuscode, url+"?"+values.Encode(), code), msgAndArgs...) 107 | } 108 | 109 | return successful 110 | } 111 | 112 | // HTTPBody is a helper that returns HTTP body of the response. It returns 113 | // empty string if building a new request fails. 114 | func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { 115 | w := httptest.NewRecorder() 116 | if len(values) > 0 { 117 | url += "?" + values.Encode() 118 | } 119 | req, err := http.NewRequest(method, url, http.NoBody) 120 | if err != nil { 121 | return "" 122 | } 123 | handler(w, req) 124 | return w.Body.String() 125 | } 126 | 127 | // HTTPBodyContains asserts that a specified handler returns a 128 | // body that contains a string. 129 | // 130 | // assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") 131 | // 132 | // Returns whether the assertion was successful (true) or not (false). 133 | func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { 134 | if h, ok := t.(tHelper); ok { 135 | h.Helper() 136 | } 137 | body := HTTPBody(handler, method, url, values) 138 | 139 | contains := strings.Contains(body, fmt.Sprint(str)) 140 | if !contains { 141 | Fail(t, fmt.Sprintf("Expected response body for %q to contain %q but found %q", url+"?"+values.Encode(), str, body), msgAndArgs...) 142 | } 143 | 144 | return contains 145 | } 146 | 147 | // HTTPBodyNotContains asserts that a specified handler returns a 148 | // body that does not contain a string. 149 | // 150 | // assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") 151 | // 152 | // Returns whether the assertion was successful (true) or not (false). 153 | func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { 154 | if h, ok := t.(tHelper); ok { 155 | h.Helper() 156 | } 157 | body := HTTPBody(handler, method, url, values) 158 | 159 | contains := strings.Contains(body, fmt.Sprint(str)) 160 | if contains { 161 | Fail(t, fmt.Sprintf("Expected response body for %q to NOT contain %q but found %q", url+"?"+values.Encode(), str, body), msgAndArgs...) 162 | } 163 | 164 | return !contains 165 | } 166 | -------------------------------------------------------------------------------- /assert/http_assertions_test.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "net/url" 8 | "testing" 9 | ) 10 | 11 | func httpOK(w http.ResponseWriter, r *http.Request) { 12 | w.WriteHeader(http.StatusOK) 13 | } 14 | 15 | func httpReadBody(w http.ResponseWriter, r *http.Request) { 16 | _, _ = io.Copy(io.Discard, r.Body) 17 | w.WriteHeader(http.StatusOK) 18 | _, _ = w.Write([]byte("hello")) 19 | } 20 | 21 | func httpRedirect(w http.ResponseWriter, r *http.Request) { 22 | w.WriteHeader(http.StatusTemporaryRedirect) 23 | } 24 | 25 | func httpError(w http.ResponseWriter, r *http.Request) { 26 | w.WriteHeader(http.StatusInternalServerError) 27 | } 28 | 29 | func httpStatusCode(w http.ResponseWriter, r *http.Request) { 30 | w.WriteHeader(http.StatusSwitchingProtocols) 31 | } 32 | 33 | func TestHTTPSuccess(t *testing.T) { 34 | t.Parallel() 35 | 36 | assert := New(t) 37 | 38 | mockT1 := new(testing.T) 39 | assert.Equal(HTTPSuccess(mockT1, httpOK, "GET", "/", nil), true) 40 | assert.False(mockT1.Failed()) 41 | 42 | mockT2 := new(testing.T) 43 | assert.Equal(HTTPSuccess(mockT2, httpRedirect, "GET", "/", nil), false) 44 | assert.True(mockT2.Failed()) 45 | 46 | mockT3 := new(mockTestingT) 47 | assert.Equal(HTTPSuccess( 48 | mockT3, httpError, "GET", "/", nil, 49 | "was not expecting a failure here", 50 | ), false) 51 | assert.True(mockT3.Failed()) 52 | assert.Contains(mockT3.errorString(), "was not expecting a failure here") 53 | 54 | mockT4 := new(testing.T) 55 | assert.Equal(HTTPSuccess(mockT4, httpStatusCode, "GET", "/", nil), false) 56 | assert.True(mockT4.Failed()) 57 | 58 | mockT5 := new(testing.T) 59 | assert.Equal(HTTPSuccess(mockT5, httpReadBody, "POST", "/", nil), true) 60 | assert.False(mockT5.Failed()) 61 | } 62 | 63 | func TestHTTPRedirect(t *testing.T) { 64 | t.Parallel() 65 | 66 | assert := New(t) 67 | 68 | mockT1 := new(mockTestingT) 69 | assert.Equal(HTTPRedirect( 70 | mockT1, httpOK, "GET", "/", nil, 71 | "was expecting a 3xx status code. Got 200.", 72 | ), false) 73 | assert.True(mockT1.Failed()) 74 | assert.Contains(mockT1.errorString(), "was expecting a 3xx status code. Got 200.") 75 | 76 | mockT2 := new(testing.T) 77 | assert.Equal(HTTPRedirect(mockT2, httpRedirect, "GET", "/", nil), true) 78 | assert.False(mockT2.Failed()) 79 | 80 | mockT3 := new(testing.T) 81 | assert.Equal(HTTPRedirect(mockT3, httpError, "GET", "/", nil), false) 82 | assert.True(mockT3.Failed()) 83 | 84 | mockT4 := new(testing.T) 85 | assert.Equal(HTTPRedirect(mockT4, httpStatusCode, "GET", "/", nil), false) 86 | assert.True(mockT4.Failed()) 87 | } 88 | 89 | func TestHTTPError(t *testing.T) { 90 | t.Parallel() 91 | 92 | assert := New(t) 93 | 94 | mockT1 := new(testing.T) 95 | assert.Equal(HTTPError(mockT1, httpOK, "GET", "/", nil), false) 96 | assert.True(mockT1.Failed()) 97 | 98 | mockT2 := new(mockTestingT) 99 | assert.Equal(HTTPError( 100 | mockT2, httpRedirect, "GET", "/", nil, 101 | "Expected this request to error out. But it didn't", 102 | ), false) 103 | assert.True(mockT2.Failed()) 104 | assert.Contains(mockT2.errorString(), "Expected this request to error out. But it didn't") 105 | 106 | mockT3 := new(testing.T) 107 | assert.Equal(HTTPError(mockT3, httpError, "GET", "/", nil), true) 108 | assert.False(mockT3.Failed()) 109 | 110 | mockT4 := new(testing.T) 111 | assert.Equal(HTTPError(mockT4, httpStatusCode, "GET", "/", nil), false) 112 | assert.True(mockT4.Failed()) 113 | } 114 | 115 | func TestHTTPStatusCode(t *testing.T) { 116 | t.Parallel() 117 | 118 | assert := New(t) 119 | 120 | mockT1 := new(testing.T) 121 | assert.Equal(HTTPStatusCode(mockT1, httpOK, "GET", "/", nil, http.StatusSwitchingProtocols), false) 122 | assert.True(mockT1.Failed()) 123 | 124 | mockT2 := new(testing.T) 125 | assert.Equal(HTTPStatusCode(mockT2, httpRedirect, "GET", "/", nil, http.StatusSwitchingProtocols), false) 126 | assert.True(mockT2.Failed()) 127 | 128 | mockT3 := new(mockTestingT) 129 | assert.Equal(HTTPStatusCode( 130 | mockT3, httpError, "GET", "/", nil, http.StatusSwitchingProtocols, 131 | "Expected the status code to be %d", http.StatusSwitchingProtocols, 132 | ), false) 133 | assert.True(mockT3.Failed()) 134 | assert.Contains(mockT3.errorString(), "Expected the status code to be 101") 135 | 136 | mockT4 := new(testing.T) 137 | assert.Equal(HTTPStatusCode(mockT4, httpStatusCode, "GET", "/", nil, http.StatusSwitchingProtocols), true) 138 | assert.False(mockT4.Failed()) 139 | } 140 | 141 | func TestHTTPStatusesWrapper(t *testing.T) { 142 | t.Parallel() 143 | 144 | assert := New(t) 145 | mockAssert := New(new(testing.T)) 146 | 147 | assert.Equal(mockAssert.HTTPSuccess(httpOK, "GET", "/", nil), true) 148 | assert.Equal(mockAssert.HTTPSuccess(httpRedirect, "GET", "/", nil), false) 149 | assert.Equal(mockAssert.HTTPSuccess(httpError, "GET", "/", nil), false) 150 | 151 | assert.Equal(mockAssert.HTTPRedirect(httpOK, "GET", "/", nil), false) 152 | assert.Equal(mockAssert.HTTPRedirect(httpRedirect, "GET", "/", nil), true) 153 | assert.Equal(mockAssert.HTTPRedirect(httpError, "GET", "/", nil), false) 154 | 155 | assert.Equal(mockAssert.HTTPError(httpOK, "GET", "/", nil), false) 156 | assert.Equal(mockAssert.HTTPError(httpRedirect, "GET", "/", nil), false) 157 | assert.Equal(mockAssert.HTTPError(httpError, "GET", "/", nil), true) 158 | } 159 | 160 | func httpHelloName(w http.ResponseWriter, r *http.Request) { 161 | name := r.FormValue("name") 162 | _, _ = fmt.Fprintf(w, "Hello, %s!", name) 163 | } 164 | 165 | func TestHTTPRequestWithNoParams(t *testing.T) { 166 | t.Parallel() 167 | 168 | var got *http.Request 169 | handler := func(w http.ResponseWriter, r *http.Request) { 170 | got = r 171 | w.WriteHeader(http.StatusOK) 172 | } 173 | 174 | True(t, HTTPSuccess(t, handler, "GET", "/url", nil)) 175 | 176 | Empty(t, got.URL.Query()) 177 | Equal(t, "/url", got.URL.RequestURI()) 178 | } 179 | 180 | func TestHTTPRequestWithParams(t *testing.T) { 181 | t.Parallel() 182 | 183 | var got *http.Request 184 | handler := func(w http.ResponseWriter, r *http.Request) { 185 | got = r 186 | w.WriteHeader(http.StatusOK) 187 | } 188 | params := url.Values{} 189 | params.Add("id", "12345") 190 | 191 | True(t, HTTPSuccess(t, handler, "GET", "/url", params)) 192 | 193 | Equal(t, url.Values{"id": []string{"12345"}}, got.URL.Query()) 194 | Equal(t, "/url?id=12345", got.URL.String()) 195 | Equal(t, "/url?id=12345", got.URL.RequestURI()) 196 | } 197 | 198 | func TestHttpBody(t *testing.T) { 199 | t.Parallel() 200 | 201 | assert := New(t) 202 | mockT := new(mockTestingT) 203 | 204 | assert.True(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) 205 | assert.True(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World")) 206 | assert.False(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) 207 | 208 | assert.False(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) 209 | assert.False(HTTPBodyNotContains( 210 | mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World", 211 | "Expected the request body to not contain 'World'. But it did.", 212 | )) 213 | assert.True(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) 214 | assert.Contains(mockT.errorString(), "Expected the request body to not contain 'World'. But it did.") 215 | 216 | assert.True(HTTPBodyContains(mockT, httpReadBody, "GET", "/", nil, "hello")) 217 | } 218 | 219 | func TestHttpBodyWrappers(t *testing.T) { 220 | t.Parallel() 221 | 222 | assert := New(t) 223 | mockAssert := New(new(testing.T)) 224 | 225 | assert.True(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) 226 | assert.True(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World")) 227 | assert.False(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) 228 | 229 | assert.False(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) 230 | assert.False(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World")) 231 | assert.True(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) 232 | } 233 | -------------------------------------------------------------------------------- /assert/internal/unsafetests/doc.go: -------------------------------------------------------------------------------- 1 | // This package exists just to isolate tests that reference the [unsafe] package. 2 | // 3 | // The tests in this package are totally safe. 4 | package unsafetests 5 | -------------------------------------------------------------------------------- /assert/internal/unsafetests/unsafetests_test.go: -------------------------------------------------------------------------------- 1 | package unsafetests_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "unsafe" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | type ignoreTestingT struct{} 12 | 13 | var _ assert.TestingT = ignoreTestingT{} 14 | 15 | func (ignoreTestingT) Helper() {} 16 | 17 | func (ignoreTestingT) Errorf(format string, args ...interface{}) { 18 | // Run the formatting, but ignore the result 19 | msg := fmt.Sprintf(format, args...) 20 | _ = msg 21 | } 22 | 23 | func TestUnsafePointers(t *testing.T) { 24 | var ignore ignoreTestingT 25 | 26 | assert.True(t, assert.Nil(t, unsafe.Pointer(nil), "unsafe.Pointer(nil) is nil")) 27 | assert.False(t, assert.NotNil(ignore, unsafe.Pointer(nil), "unsafe.Pointer(nil) is nil")) 28 | 29 | assert.True(t, assert.Nil(t, unsafe.Pointer((*int)(nil)), "unsafe.Pointer((*int)(nil)) is nil")) 30 | assert.False(t, assert.NotNil(ignore, unsafe.Pointer((*int)(nil)), "unsafe.Pointer((*int)(nil)) is nil")) 31 | 32 | assert.False(t, assert.Nil(ignore, unsafe.Pointer(new(int)), "unsafe.Pointer(new(int)) is NOT nil")) 33 | assert.True(t, assert.NotNil(t, unsafe.Pointer(new(int)), "unsafe.Pointer(new(int)) is NOT nil")) 34 | } 35 | -------------------------------------------------------------------------------- /assert/yaml/yaml_custom.go: -------------------------------------------------------------------------------- 1 | //go:build testify_yaml_custom && !testify_yaml_fail && !testify_yaml_default 2 | 3 | // Package yaml is an implementation of YAML functions that calls a pluggable implementation. 4 | // 5 | // This implementation is selected with the testify_yaml_custom build tag. 6 | // 7 | // go test -tags testify_yaml_custom 8 | // 9 | // This implementation can be used at build time to replace the default implementation 10 | // to avoid linking with [gopkg.in/yaml.v3]. 11 | // 12 | // In your test package: 13 | // 14 | // import assertYaml "github.com/stretchr/testify/assert/yaml" 15 | // 16 | // func init() { 17 | // assertYaml.Unmarshal = func (in []byte, out interface{}) error { 18 | // // ... 19 | // return nil 20 | // } 21 | // } 22 | package yaml 23 | 24 | var Unmarshal func(in []byte, out interface{}) error 25 | -------------------------------------------------------------------------------- /assert/yaml/yaml_default.go: -------------------------------------------------------------------------------- 1 | //go:build !testify_yaml_fail && !testify_yaml_custom 2 | 3 | // Package yaml is just an indirection to handle YAML deserialization. 4 | // 5 | // This package is just an indirection that allows the builder to override the 6 | // indirection with an alternative implementation of this package that uses 7 | // another implementation of YAML deserialization. This allows to not either not 8 | // use YAML deserialization at all, or to use another implementation than 9 | // [gopkg.in/yaml.v3] (for example for license compatibility reasons, see [PR #1120]). 10 | // 11 | // Alternative implementations are selected using build tags: 12 | // 13 | // - testify_yaml_fail: [Unmarshal] always fails with an error 14 | // - testify_yaml_custom: [Unmarshal] is a variable. Caller must initialize it 15 | // before calling any of [github.com/stretchr/testify/assert.YAMLEq] or 16 | // [github.com/stretchr/testify/assert.YAMLEqf]. 17 | // 18 | // Usage: 19 | // 20 | // go test -tags testify_yaml_fail 21 | // 22 | // You can check with "go list" which implementation is linked: 23 | // 24 | // go list -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml 25 | // go list -tags testify_yaml_fail -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml 26 | // go list -tags testify_yaml_custom -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml 27 | // 28 | // [PR #1120]: https://github.com/stretchr/testify/pull/1120 29 | package yaml 30 | 31 | import goyaml "gopkg.in/yaml.v3" 32 | 33 | // Unmarshal is just a wrapper of [gopkg.in/yaml.v3.Unmarshal]. 34 | func Unmarshal(in []byte, out interface{}) error { 35 | return goyaml.Unmarshal(in, out) 36 | } 37 | -------------------------------------------------------------------------------- /assert/yaml/yaml_fail.go: -------------------------------------------------------------------------------- 1 | //go:build testify_yaml_fail && !testify_yaml_custom && !testify_yaml_default 2 | 3 | // Package yaml is an implementation of YAML functions that always fail. 4 | // 5 | // This implementation can be used at build time to replace the default implementation 6 | // to avoid linking with [gopkg.in/yaml.v3]: 7 | // 8 | // go test -tags testify_yaml_fail 9 | package yaml 10 | 11 | import "errors" 12 | 13 | var errNotImplemented = errors.New("YAML functions are not available (see https://pkg.go.dev/github.com/stretchr/testify/assert/yaml)") 14 | 15 | func Unmarshal([]byte, interface{}) error { 16 | return errNotImplemented 17 | } 18 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Module testify is a set of packages that provide many tools for testifying that your code will behave as you intend. 2 | // 3 | // Testify contains the following packages: 4 | // 5 | // The [github.com/stretchr/testify/assert] package provides a comprehensive set of assertion functions that tie in to [the Go testing system]. 6 | // The [github.com/stretchr/testify/require] package provides the same assertions but as fatal checks. 7 | // 8 | // 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. 9 | // 10 | // 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. 11 | // 12 | // A [golangci-lint] compatible linter for testify is available called [testifylint]. 13 | // 14 | // [the Go testing system]: https://go.dev/doc/code#Testing 15 | // [golangci-lint]: https://golangci-lint.run/ 16 | // [testifylint]: https://github.com/Antonboom/testifylint 17 | package testify 18 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/stretchr/testify 2 | 3 | // This should match the minimum supported version that is tested in 4 | // .github/workflows/main.yml 5 | go 1.17 6 | 7 | require ( 8 | github.com/davecgh/go-spew v1.1.1 9 | github.com/pmezard/go-difflib v1.0.0 10 | github.com/stretchr/objx v0.5.2 // To avoid a cycle the version of testify used by objx should be excluded below 11 | gopkg.in/yaml.v3 v3.0.1 12 | ) 13 | 14 | // Break dependency cycle with objx. 15 | // See https://github.com/stretchr/objx/pull/140 16 | exclude github.com/stretchr/testify v1.8.4 17 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= 6 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 7 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 8 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 9 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 10 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 11 | -------------------------------------------------------------------------------- /http/doc.go: -------------------------------------------------------------------------------- 1 | // Deprecated: Use [net/http/httptest] instead. 2 | package http 3 | -------------------------------------------------------------------------------- /http/test_response_writer.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | // Deprecated: Use [net/http/httptest] instead. 8 | type TestResponseWriter struct { 9 | 10 | // StatusCode is the last int written by the call to WriteHeader(int) 11 | StatusCode int 12 | 13 | // Output is a string containing the written bytes using the Write([]byte) func. 14 | Output string 15 | 16 | // header is the internal storage of the http.Header object 17 | header http.Header 18 | } 19 | 20 | // Deprecated: Use [net/http/httptest] instead. 21 | func (rw *TestResponseWriter) Header() http.Header { 22 | 23 | if rw.header == nil { 24 | rw.header = make(http.Header) 25 | } 26 | 27 | return rw.header 28 | } 29 | 30 | // Deprecated: Use [net/http/httptest] instead. 31 | func (rw *TestResponseWriter) Write(bytes []byte) (int, error) { 32 | 33 | // assume 200 success if no header has been set 34 | if rw.StatusCode == 0 { 35 | rw.WriteHeader(200) 36 | } 37 | 38 | // add these bytes to the output string 39 | rw.Output += string(bytes) 40 | 41 | // return normal values 42 | return 0, nil 43 | 44 | } 45 | 46 | // Deprecated: Use [net/http/httptest] instead. 47 | func (rw *TestResponseWriter) WriteHeader(i int) { 48 | rw.StatusCode = i 49 | } 50 | -------------------------------------------------------------------------------- /http/test_round_tripper.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/stretchr/testify/mock" 7 | ) 8 | 9 | // Deprecated: Use [net/http/httptest] instead. 10 | type TestRoundTripper struct { 11 | mock.Mock 12 | } 13 | 14 | // Deprecated: Use [net/http/httptest] instead. 15 | func (t *TestRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { 16 | args := t.Called(req) 17 | return args.Get(0).(*http.Response), args.Error(1) 18 | } 19 | -------------------------------------------------------------------------------- /mock/doc.go: -------------------------------------------------------------------------------- 1 | // Package mock provides a system by which it is possible to mock your objects 2 | // and verify calls are happening as expected. 3 | // 4 | // # Example Usage 5 | // 6 | // The mock package provides an object, Mock, that tracks activity on another object. It is usually 7 | // embedded into a test object as shown below: 8 | // 9 | // type MyTestObject struct { 10 | // // add a Mock object instance 11 | // mock.Mock 12 | // 13 | // // other fields go here as normal 14 | // } 15 | // 16 | // When implementing the methods of an interface, you wire your functions up 17 | // to call the Mock.Called(args...) method, and return the appropriate values. 18 | // 19 | // For example, to mock a method that saves the name and age of a person and returns 20 | // the year of their birth or an error, you might write this: 21 | // 22 | // func (o *MyTestObject) SavePersonDetails(firstname, lastname string, age int) (int, error) { 23 | // args := o.Called(firstname, lastname, age) 24 | // return args.Int(0), args.Error(1) 25 | // } 26 | // 27 | // The Int, Error and Bool methods are examples of strongly typed getters that take the argument 28 | // index position. Given this argument list: 29 | // 30 | // (12, true, "Something") 31 | // 32 | // You could read them out strongly typed like this: 33 | // 34 | // args.Int(0) 35 | // args.Bool(1) 36 | // args.String(2) 37 | // 38 | // For objects of your own type, use the generic Arguments.Get(index) method and make a type assertion: 39 | // 40 | // return args.Get(0).(*MyObject), args.Get(1).(*AnotherObjectOfMine) 41 | // 42 | // This may cause a panic if the object you are getting is nil (the type assertion will fail), in those 43 | // cases you should check for nil first. 44 | package mock 45 | -------------------------------------------------------------------------------- /package_test.go: -------------------------------------------------------------------------------- 1 | package testify 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestImports(t *testing.T) { 10 | if assert.Equal(t, 1, 1) != true { 11 | t.Error("Something is wrong.") 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /require/doc.go: -------------------------------------------------------------------------------- 1 | // Package require implements the same assertions as the `assert` package but 2 | // stops test execution when a test fails. 3 | // 4 | // # Example Usage 5 | // 6 | // The following is a complete example using require in a standard test function: 7 | // 8 | // import ( 9 | // "testing" 10 | // "github.com/stretchr/testify/require" 11 | // ) 12 | // 13 | // func TestSomething(t *testing.T) { 14 | // 15 | // var a string = "Hello" 16 | // var b string = "Hello" 17 | // 18 | // require.Equal(t, a, b, "The two words should be the same.") 19 | // 20 | // } 21 | // 22 | // # Assertions 23 | // 24 | // The `require` package have same global functions as in the `assert` package, 25 | // but instead of returning a boolean result they call `t.FailNow()`. 26 | // A consequence of this is that it must be called from the goroutine running 27 | // the test function, not from other goroutines created during the test. 28 | // 29 | // Every assertion function also takes an optional string message as the final argument, 30 | // allowing custom error messages to be appended to the message the assertion method outputs. 31 | package require 32 | -------------------------------------------------------------------------------- /require/forward_requirements.go: -------------------------------------------------------------------------------- 1 | package require 2 | 3 | // Assertions provides assertion methods around the 4 | // TestingT interface. 5 | type Assertions struct { 6 | t TestingT 7 | } 8 | 9 | // New makes a new Assertions object for the specified TestingT. 10 | func New(t TestingT) *Assertions { 11 | return &Assertions{ 12 | t: t, 13 | } 14 | } 15 | 16 | //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=require -template=require_forward.go.tmpl -include-format-funcs" 17 | -------------------------------------------------------------------------------- /require/forward_requirements_test.go: -------------------------------------------------------------------------------- 1 | package require 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestImplementsWrapper(t *testing.T) { 10 | t.Parallel() 11 | 12 | require := New(t) 13 | 14 | require.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject)) 15 | 16 | mockT := new(MockT) 17 | mockRequire := New(mockT) 18 | mockRequire.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterNonConformingObject)) 19 | if !mockT.Failed { 20 | t.Error("Check should fail") 21 | } 22 | } 23 | 24 | func TestIsNotTypeWrapper(t *testing.T) { 25 | t.Parallel() 26 | 27 | require := New(t) 28 | require.IsNotType(new(AssertionTesterNonConformingObject), new(AssertionTesterConformingObject)) 29 | 30 | mockT := new(MockT) 31 | mockRequire := New(mockT) 32 | mockRequire.IsNotType(new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) 33 | if !mockT.Failed { 34 | t.Error("Check should fail") 35 | } 36 | } 37 | 38 | func TestIsTypeWrapper(t *testing.T) { 39 | t.Parallel() 40 | 41 | require := New(t) 42 | require.IsType(new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) 43 | 44 | mockT := new(MockT) 45 | mockRequire := New(mockT) 46 | mockRequire.IsType(new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject)) 47 | if !mockT.Failed { 48 | t.Error("Check should fail") 49 | } 50 | } 51 | 52 | func TestEqualWrapper(t *testing.T) { 53 | t.Parallel() 54 | 55 | require := New(t) 56 | require.Equal(1, 1) 57 | 58 | mockT := new(MockT) 59 | mockRequire := New(mockT) 60 | mockRequire.Equal(1, 2) 61 | if !mockT.Failed { 62 | t.Error("Check should fail") 63 | } 64 | } 65 | 66 | func TestNotEqualWrapper(t *testing.T) { 67 | t.Parallel() 68 | 69 | require := New(t) 70 | require.NotEqual(1, 2) 71 | 72 | mockT := new(MockT) 73 | mockRequire := New(mockT) 74 | mockRequire.NotEqual(2, 2) 75 | if !mockT.Failed { 76 | t.Error("Check should fail") 77 | } 78 | } 79 | 80 | func TestExactlyWrapper(t *testing.T) { 81 | t.Parallel() 82 | 83 | require := New(t) 84 | 85 | a := float32(1) 86 | b := float32(1) 87 | c := float64(1) 88 | 89 | require.Exactly(a, b) 90 | 91 | mockT := new(MockT) 92 | mockRequire := New(mockT) 93 | mockRequire.Exactly(a, c) 94 | if !mockT.Failed { 95 | t.Error("Check should fail") 96 | } 97 | } 98 | 99 | func TestNotNilWrapper(t *testing.T) { 100 | t.Parallel() 101 | 102 | require := New(t) 103 | require.NotNil(t, new(AssertionTesterConformingObject)) 104 | 105 | mockT := new(MockT) 106 | mockRequire := New(mockT) 107 | mockRequire.NotNil(nil) 108 | if !mockT.Failed { 109 | t.Error("Check should fail") 110 | } 111 | } 112 | 113 | func TestNilWrapper(t *testing.T) { 114 | t.Parallel() 115 | 116 | require := New(t) 117 | require.Nil(nil) 118 | 119 | mockT := new(MockT) 120 | mockRequire := New(mockT) 121 | mockRequire.Nil(new(AssertionTesterConformingObject)) 122 | if !mockT.Failed { 123 | t.Error("Check should fail") 124 | } 125 | } 126 | 127 | func TestTrueWrapper(t *testing.T) { 128 | t.Parallel() 129 | 130 | require := New(t) 131 | require.True(true) 132 | 133 | mockT := new(MockT) 134 | mockRequire := New(mockT) 135 | mockRequire.True(false) 136 | if !mockT.Failed { 137 | t.Error("Check should fail") 138 | } 139 | } 140 | 141 | func TestFalseWrapper(t *testing.T) { 142 | t.Parallel() 143 | 144 | require := New(t) 145 | require.False(false) 146 | 147 | mockT := new(MockT) 148 | mockRequire := New(mockT) 149 | mockRequire.False(true) 150 | if !mockT.Failed { 151 | t.Error("Check should fail") 152 | } 153 | } 154 | 155 | func TestContainsWrapper(t *testing.T) { 156 | t.Parallel() 157 | 158 | require := New(t) 159 | require.Contains("Hello World", "Hello") 160 | 161 | mockT := new(MockT) 162 | mockRequire := New(mockT) 163 | mockRequire.Contains("Hello World", "Salut") 164 | if !mockT.Failed { 165 | t.Error("Check should fail") 166 | } 167 | } 168 | 169 | func TestNotContainsWrapper(t *testing.T) { 170 | t.Parallel() 171 | 172 | require := New(t) 173 | require.NotContains("Hello World", "Hello!") 174 | 175 | mockT := new(MockT) 176 | mockRequire := New(mockT) 177 | mockRequire.NotContains("Hello World", "Hello") 178 | if !mockT.Failed { 179 | t.Error("Check should fail") 180 | } 181 | } 182 | 183 | func TestPanicsWrapper(t *testing.T) { 184 | t.Parallel() 185 | 186 | require := New(t) 187 | require.Panics(func() { 188 | panic("Panic!") 189 | }) 190 | 191 | mockT := new(MockT) 192 | mockRequire := New(mockT) 193 | mockRequire.Panics(func() {}) 194 | if !mockT.Failed { 195 | t.Error("Check should fail") 196 | } 197 | } 198 | 199 | func TestNotPanicsWrapper(t *testing.T) { 200 | t.Parallel() 201 | 202 | require := New(t) 203 | require.NotPanics(func() {}) 204 | 205 | mockT := new(MockT) 206 | mockRequire := New(mockT) 207 | mockRequire.NotPanics(func() { 208 | panic("Panic!") 209 | }) 210 | if !mockT.Failed { 211 | t.Error("Check should fail") 212 | } 213 | } 214 | 215 | func TestNoErrorWrapper(t *testing.T) { 216 | t.Parallel() 217 | 218 | require := New(t) 219 | require.NoError(nil) 220 | 221 | mockT := new(MockT) 222 | mockRequire := New(mockT) 223 | mockRequire.NoError(errors.New("some error")) 224 | if !mockT.Failed { 225 | t.Error("Check should fail") 226 | } 227 | } 228 | 229 | func TestErrorWrapper(t *testing.T) { 230 | t.Parallel() 231 | 232 | require := New(t) 233 | require.Error(errors.New("some error")) 234 | 235 | mockT := new(MockT) 236 | mockRequire := New(mockT) 237 | mockRequire.Error(nil) 238 | if !mockT.Failed { 239 | t.Error("Check should fail") 240 | } 241 | } 242 | 243 | func TestErrorContainsWrapper(t *testing.T) { 244 | t.Parallel() 245 | 246 | require := New(t) 247 | require.ErrorContains(errors.New("some error: another error"), "some error") 248 | 249 | mockT := new(MockT) 250 | mockRequire := New(mockT) 251 | mockRequire.ErrorContains(errors.New("some error: another error"), "different error") 252 | if !mockT.Failed { 253 | t.Error("Check should fail") 254 | } 255 | } 256 | 257 | func TestEqualErrorWrapper(t *testing.T) { 258 | t.Parallel() 259 | 260 | require := New(t) 261 | require.EqualError(errors.New("some error"), "some error") 262 | 263 | mockT := new(MockT) 264 | mockRequire := New(mockT) 265 | mockRequire.EqualError(errors.New("some error"), "Not some error") 266 | if !mockT.Failed { 267 | t.Error("Check should fail") 268 | } 269 | } 270 | 271 | func TestEmptyWrapper(t *testing.T) { 272 | t.Parallel() 273 | 274 | require := New(t) 275 | require.Empty("") 276 | 277 | mockT := new(MockT) 278 | mockRequire := New(mockT) 279 | mockRequire.Empty("x") 280 | if !mockT.Failed { 281 | t.Error("Check should fail") 282 | } 283 | } 284 | 285 | func TestNotEmptyWrapper(t *testing.T) { 286 | t.Parallel() 287 | 288 | require := New(t) 289 | require.NotEmpty("x") 290 | 291 | mockT := new(MockT) 292 | mockRequire := New(mockT) 293 | mockRequire.NotEmpty("") 294 | if !mockT.Failed { 295 | t.Error("Check should fail") 296 | } 297 | } 298 | 299 | func TestWithinDurationWrapper(t *testing.T) { 300 | t.Parallel() 301 | 302 | require := New(t) 303 | a := time.Now() 304 | b := a.Add(10 * time.Second) 305 | 306 | require.WithinDuration(a, b, 15*time.Second) 307 | 308 | mockT := new(MockT) 309 | mockRequire := New(mockT) 310 | mockRequire.WithinDuration(a, b, 5*time.Second) 311 | if !mockT.Failed { 312 | t.Error("Check should fail") 313 | } 314 | } 315 | 316 | func TestInDeltaWrapper(t *testing.T) { 317 | t.Parallel() 318 | 319 | require := New(t) 320 | require.InDelta(1.001, 1, 0.01) 321 | 322 | mockT := new(MockT) 323 | mockRequire := New(mockT) 324 | mockRequire.InDelta(1, 2, 0.5) 325 | if !mockT.Failed { 326 | t.Error("Check should fail") 327 | } 328 | } 329 | 330 | func TestZeroWrapper(t *testing.T) { 331 | t.Parallel() 332 | 333 | require := New(t) 334 | require.Zero(0) 335 | 336 | mockT := new(MockT) 337 | mockRequire := New(mockT) 338 | mockRequire.Zero(1) 339 | if !mockT.Failed { 340 | t.Error("Check should fail") 341 | } 342 | } 343 | 344 | func TestNotZeroWrapper(t *testing.T) { 345 | t.Parallel() 346 | 347 | require := New(t) 348 | require.NotZero(1) 349 | 350 | mockT := new(MockT) 351 | mockRequire := New(mockT) 352 | mockRequire.NotZero(0) 353 | if !mockT.Failed { 354 | t.Error("Check should fail") 355 | } 356 | } 357 | 358 | func TestJSONEqWrapper_EqualSONString(t *testing.T) { 359 | t.Parallel() 360 | 361 | mockT := new(MockT) 362 | mockRequire := New(mockT) 363 | 364 | mockRequire.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) 365 | if mockT.Failed { 366 | t.Error("Check should pass") 367 | } 368 | } 369 | 370 | func TestJSONEqWrapper_EquivalentButNotEqual(t *testing.T) { 371 | t.Parallel() 372 | 373 | mockT := new(MockT) 374 | mockRequire := New(mockT) 375 | 376 | mockRequire.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) 377 | if mockT.Failed { 378 | t.Error("Check should pass") 379 | } 380 | } 381 | 382 | func TestJSONEqWrapper_HashOfArraysAndHashes(t *testing.T) { 383 | t.Parallel() 384 | 385 | mockT := new(MockT) 386 | mockRequire := New(mockT) 387 | 388 | mockRequire.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}", 389 | "{\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}") 390 | if mockT.Failed { 391 | t.Error("Check should pass") 392 | } 393 | } 394 | 395 | func TestJSONEqWrapper_Array(t *testing.T) { 396 | t.Parallel() 397 | 398 | mockT := new(MockT) 399 | mockRequire := New(mockT) 400 | 401 | mockRequire.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) 402 | if mockT.Failed { 403 | t.Error("Check should pass") 404 | } 405 | } 406 | 407 | func TestJSONEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { 408 | t.Parallel() 409 | 410 | mockT := new(MockT) 411 | mockRequire := New(mockT) 412 | 413 | mockRequire.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) 414 | if !mockT.Failed { 415 | t.Error("Check should fail") 416 | } 417 | } 418 | 419 | func TestJSONEqWrapper_HashesNotEquivalent(t *testing.T) { 420 | t.Parallel() 421 | 422 | mockT := new(MockT) 423 | mockRequire := New(mockT) 424 | 425 | mockRequire.JSONEq(`{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) 426 | if !mockT.Failed { 427 | t.Error("Check should fail") 428 | } 429 | } 430 | 431 | func TestJSONEqWrapper_ActualIsNotJSON(t *testing.T) { 432 | t.Parallel() 433 | 434 | mockT := new(MockT) 435 | mockRequire := New(mockT) 436 | 437 | mockRequire.JSONEq(`{"foo": "bar"}`, "Not JSON") 438 | if !mockT.Failed { 439 | t.Error("Check should fail") 440 | } 441 | } 442 | 443 | func TestJSONEqWrapper_ExpectedIsNotJSON(t *testing.T) { 444 | t.Parallel() 445 | 446 | mockT := new(MockT) 447 | mockRequire := New(mockT) 448 | 449 | mockRequire.JSONEq("Not JSON", `{"foo": "bar", "hello": "world"}`) 450 | if !mockT.Failed { 451 | t.Error("Check should fail") 452 | } 453 | } 454 | 455 | func TestJSONEqWrapper_ExpectedAndActualNotJSON(t *testing.T) { 456 | t.Parallel() 457 | 458 | mockT := new(MockT) 459 | mockRequire := New(mockT) 460 | 461 | mockRequire.JSONEq("Not JSON", "Not JSON") 462 | if !mockT.Failed { 463 | t.Error("Check should fail") 464 | } 465 | } 466 | 467 | func TestJSONEqWrapper_ArraysOfDifferentOrder(t *testing.T) { 468 | t.Parallel() 469 | 470 | mockT := new(MockT) 471 | mockRequire := New(mockT) 472 | 473 | mockRequire.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) 474 | if !mockT.Failed { 475 | t.Error("Check should fail") 476 | } 477 | } 478 | 479 | func TestYAMLEqWrapper_EqualYAMLString(t *testing.T) { 480 | t.Parallel() 481 | 482 | mockT := new(MockT) 483 | mockRequire := New(mockT) 484 | 485 | mockRequire.YAMLEq(`{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) 486 | if mockT.Failed { 487 | t.Error("Check should pass") 488 | } 489 | } 490 | 491 | func TestYAMLEqWrapper_EquivalentButNotEqual(t *testing.T) { 492 | t.Parallel() 493 | 494 | mockT := new(MockT) 495 | mockRequire := New(mockT) 496 | 497 | mockRequire.YAMLEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) 498 | if mockT.Failed { 499 | t.Error("Check should pass") 500 | } 501 | } 502 | 503 | func TestYAMLEqWrapper_HashOfArraysAndHashes(t *testing.T) { 504 | t.Parallel() 505 | 506 | mockT := new(MockT) 507 | mockRequire := New(mockT) 508 | 509 | expected := ` 510 | numeric: 1.5 511 | array: 512 | - foo: bar 513 | - 1 514 | - "string" 515 | - ["nested", "array", 5.5] 516 | hash: 517 | nested: hash 518 | nested_slice: [this, is, nested] 519 | string: "foo" 520 | ` 521 | 522 | actual := ` 523 | numeric: 1.5 524 | hash: 525 | nested: hash 526 | nested_slice: [this, is, nested] 527 | string: "foo" 528 | array: 529 | - foo: bar 530 | - 1 531 | - "string" 532 | - ["nested", "array", 5.5] 533 | ` 534 | 535 | mockRequire.YAMLEq(expected, actual) 536 | if mockT.Failed { 537 | t.Error("Check should pass") 538 | } 539 | } 540 | 541 | func TestYAMLEqWrapper_Array(t *testing.T) { 542 | t.Parallel() 543 | 544 | mockT := new(MockT) 545 | mockRequire := New(mockT) 546 | 547 | mockRequire.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) 548 | if mockT.Failed { 549 | t.Error("Check should pass") 550 | } 551 | } 552 | 553 | func TestYAMLEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { 554 | t.Parallel() 555 | 556 | mockT := new(MockT) 557 | mockRequire := New(mockT) 558 | 559 | mockRequire.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) 560 | if !mockT.Failed { 561 | t.Error("Check should fail") 562 | } 563 | } 564 | 565 | func TestYAMLEqWrapper_HashesNotEquivalent(t *testing.T) { 566 | t.Parallel() 567 | 568 | mockT := new(MockT) 569 | mockRequire := New(mockT) 570 | 571 | mockRequire.YAMLEq(`{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) 572 | if !mockT.Failed { 573 | t.Error("Check should fail") 574 | } 575 | } 576 | 577 | func TestYAMLEqWrapper_ActualIsSimpleString(t *testing.T) { 578 | t.Parallel() 579 | 580 | mockT := new(MockT) 581 | mockRequire := New(mockT) 582 | 583 | mockRequire.YAMLEq(`{"foo": "bar"}`, "Simple String") 584 | if !mockT.Failed { 585 | t.Error("Check should fail") 586 | } 587 | } 588 | 589 | func TestYAMLEqWrapper_ExpectedIsSimpleString(t *testing.T) { 590 | t.Parallel() 591 | 592 | mockT := new(MockT) 593 | mockRequire := New(mockT) 594 | 595 | mockRequire.YAMLEq("Simple String", `{"foo": "bar", "hello": "world"}`) 596 | if !mockT.Failed { 597 | t.Error("Check should fail") 598 | } 599 | } 600 | 601 | func TestYAMLEqWrapper_ExpectedAndActualSimpleString(t *testing.T) { 602 | t.Parallel() 603 | 604 | mockT := new(MockT) 605 | mockRequire := New(mockT) 606 | 607 | mockRequire.YAMLEq("Simple String", "Simple String") 608 | if mockT.Failed { 609 | t.Error("Check should pass") 610 | } 611 | } 612 | 613 | func TestYAMLEqWrapper_ArraysOfDifferentOrder(t *testing.T) { 614 | t.Parallel() 615 | 616 | mockT := new(MockT) 617 | mockRequire := New(mockT) 618 | 619 | mockRequire.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) 620 | if !mockT.Failed { 621 | t.Error("Check should fail") 622 | } 623 | } 624 | -------------------------------------------------------------------------------- /require/require.go.tmpl: -------------------------------------------------------------------------------- 1 | {{ replace .Comment "assert." "require."}} 2 | func {{.DocInfo.Name}}(t TestingT, {{.Params}}) { 3 | if h, ok := t.(tHelper); ok { h.Helper() } 4 | if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return } 5 | t.FailNow() 6 | } 7 | -------------------------------------------------------------------------------- /require/require_forward.go.tmpl: -------------------------------------------------------------------------------- 1 | {{.CommentWithoutT "a"}} 2 | func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) { 3 | if h, ok := a.t.(tHelper); ok { h.Helper() } 4 | {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) 5 | } 6 | -------------------------------------------------------------------------------- /require/requirements.go: -------------------------------------------------------------------------------- 1 | package require 2 | 3 | // TestingT is an interface wrapper around *testing.T 4 | type TestingT interface { 5 | Errorf(format string, args ...interface{}) 6 | FailNow() 7 | } 8 | 9 | type tHelper = interface { 10 | Helper() 11 | } 12 | 13 | // ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful 14 | // for table driven tests. 15 | type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{}) 16 | 17 | // ValueAssertionFunc is a common function prototype when validating a single value. Can be useful 18 | // for table driven tests. 19 | type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) 20 | 21 | // BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful 22 | // for table driven tests. 23 | type BoolAssertionFunc func(TestingT, bool, ...interface{}) 24 | 25 | // ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful 26 | // for table driven tests. 27 | type ErrorAssertionFunc func(TestingT, error, ...interface{}) 28 | 29 | //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=require -template=require.go.tmpl -include-format-funcs" 30 | -------------------------------------------------------------------------------- /require/requirements_test.go: -------------------------------------------------------------------------------- 1 | package require 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "testing" 7 | "time" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | // AssertionTesterInterface defines an interface to be used for testing assertion methods 13 | type AssertionTesterInterface interface { 14 | TestMethod() 15 | } 16 | 17 | // AssertionTesterConformingObject is an object that conforms to the AssertionTesterInterface interface 18 | type AssertionTesterConformingObject struct { 19 | } 20 | 21 | func (a *AssertionTesterConformingObject) TestMethod() { 22 | } 23 | 24 | // AssertionTesterNonConformingObject is an object that does not conform to the AssertionTesterInterface interface 25 | type AssertionTesterNonConformingObject struct { 26 | } 27 | 28 | type MockT struct { 29 | Failed bool 30 | } 31 | 32 | // Helper is like [testing.T.Helper] but does nothing. 33 | func (MockT) Helper() {} 34 | 35 | func (t *MockT) FailNow() { 36 | t.Failed = true 37 | } 38 | 39 | func (t *MockT) Errorf(format string, args ...interface{}) { 40 | _, _ = format, args 41 | } 42 | 43 | func TestImplements(t *testing.T) { 44 | t.Parallel() 45 | 46 | Implements(t, (*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject)) 47 | 48 | mockT := new(MockT) 49 | Implements(mockT, (*AssertionTesterInterface)(nil), new(AssertionTesterNonConformingObject)) 50 | if !mockT.Failed { 51 | t.Error("Check should fail") 52 | } 53 | } 54 | 55 | func TestIsType(t *testing.T) { 56 | t.Parallel() 57 | 58 | IsType(t, new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) 59 | 60 | mockT := new(MockT) 61 | IsType(mockT, new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject)) 62 | if !mockT.Failed { 63 | t.Error("Check should fail") 64 | } 65 | } 66 | 67 | func TestEqual(t *testing.T) { 68 | t.Parallel() 69 | 70 | Equal(t, 1, 1) 71 | 72 | mockT := new(MockT) 73 | Equal(mockT, 1, 2) 74 | if !mockT.Failed { 75 | t.Error("Check should fail") 76 | } 77 | 78 | } 79 | 80 | func TestNotEqual(t *testing.T) { 81 | t.Parallel() 82 | 83 | NotEqual(t, 1, 2) 84 | mockT := new(MockT) 85 | NotEqual(mockT, 2, 2) 86 | if !mockT.Failed { 87 | t.Error("Check should fail") 88 | } 89 | } 90 | 91 | func TestExactly(t *testing.T) { 92 | t.Parallel() 93 | 94 | a := float32(1) 95 | b := float32(1) 96 | c := float64(1) 97 | 98 | Exactly(t, a, b) 99 | 100 | mockT := new(MockT) 101 | Exactly(mockT, a, c) 102 | if !mockT.Failed { 103 | t.Error("Check should fail") 104 | } 105 | } 106 | 107 | func TestNotNil(t *testing.T) { 108 | t.Parallel() 109 | 110 | NotNil(t, new(AssertionTesterConformingObject)) 111 | 112 | mockT := new(MockT) 113 | NotNil(mockT, nil) 114 | if !mockT.Failed { 115 | t.Error("Check should fail") 116 | } 117 | } 118 | 119 | func TestNil(t *testing.T) { 120 | t.Parallel() 121 | 122 | Nil(t, nil) 123 | 124 | mockT := new(MockT) 125 | Nil(mockT, new(AssertionTesterConformingObject)) 126 | if !mockT.Failed { 127 | t.Error("Check should fail") 128 | } 129 | } 130 | 131 | func TestTrue(t *testing.T) { 132 | t.Parallel() 133 | 134 | True(t, true) 135 | 136 | mockT := new(MockT) 137 | True(mockT, false) 138 | if !mockT.Failed { 139 | t.Error("Check should fail") 140 | } 141 | } 142 | 143 | func TestFalse(t *testing.T) { 144 | t.Parallel() 145 | 146 | False(t, false) 147 | 148 | mockT := new(MockT) 149 | False(mockT, true) 150 | if !mockT.Failed { 151 | t.Error("Check should fail") 152 | } 153 | } 154 | 155 | func TestContains(t *testing.T) { 156 | t.Parallel() 157 | 158 | Contains(t, "Hello World", "Hello") 159 | 160 | mockT := new(MockT) 161 | Contains(mockT, "Hello World", "Salut") 162 | if !mockT.Failed { 163 | t.Error("Check should fail") 164 | } 165 | } 166 | 167 | func TestNotContains(t *testing.T) { 168 | t.Parallel() 169 | 170 | NotContains(t, "Hello World", "Hello!") 171 | 172 | mockT := new(MockT) 173 | NotContains(mockT, "Hello World", "Hello") 174 | if !mockT.Failed { 175 | t.Error("Check should fail") 176 | } 177 | } 178 | 179 | func TestPanics(t *testing.T) { 180 | t.Parallel() 181 | 182 | Panics(t, func() { 183 | panic("Panic!") 184 | }) 185 | 186 | mockT := new(MockT) 187 | Panics(mockT, func() {}) 188 | if !mockT.Failed { 189 | t.Error("Check should fail") 190 | } 191 | } 192 | 193 | func TestNotPanics(t *testing.T) { 194 | t.Parallel() 195 | 196 | NotPanics(t, func() {}) 197 | 198 | mockT := new(MockT) 199 | NotPanics(mockT, func() { 200 | panic("Panic!") 201 | }) 202 | if !mockT.Failed { 203 | t.Error("Check should fail") 204 | } 205 | } 206 | 207 | func TestNoError(t *testing.T) { 208 | t.Parallel() 209 | 210 | NoError(t, nil) 211 | 212 | mockT := new(MockT) 213 | NoError(mockT, errors.New("some error")) 214 | if !mockT.Failed { 215 | t.Error("Check should fail") 216 | } 217 | } 218 | 219 | func TestError(t *testing.T) { 220 | t.Parallel() 221 | 222 | Error(t, errors.New("some error")) 223 | 224 | mockT := new(MockT) 225 | Error(mockT, nil) 226 | if !mockT.Failed { 227 | t.Error("Check should fail") 228 | } 229 | } 230 | 231 | func TestErrorContains(t *testing.T) { 232 | t.Parallel() 233 | 234 | ErrorContains(t, errors.New("some error: another error"), "some error") 235 | 236 | mockT := new(MockT) 237 | ErrorContains(mockT, errors.New("some error"), "different error") 238 | if !mockT.Failed { 239 | t.Error("Check should fail") 240 | } 241 | } 242 | 243 | func TestEqualError(t *testing.T) { 244 | t.Parallel() 245 | 246 | EqualError(t, errors.New("some error"), "some error") 247 | 248 | mockT := new(MockT) 249 | EqualError(mockT, errors.New("some error"), "Not some error") 250 | if !mockT.Failed { 251 | t.Error("Check should fail") 252 | } 253 | } 254 | 255 | func TestEmpty(t *testing.T) { 256 | t.Parallel() 257 | 258 | Empty(t, "") 259 | 260 | mockT := new(MockT) 261 | Empty(mockT, "x") 262 | if !mockT.Failed { 263 | t.Error("Check should fail") 264 | } 265 | } 266 | 267 | func TestNotEmpty(t *testing.T) { 268 | t.Parallel() 269 | 270 | NotEmpty(t, "x") 271 | 272 | mockT := new(MockT) 273 | NotEmpty(mockT, "") 274 | if !mockT.Failed { 275 | t.Error("Check should fail") 276 | } 277 | } 278 | 279 | func TestWithinDuration(t *testing.T) { 280 | t.Parallel() 281 | 282 | a := time.Now() 283 | b := a.Add(10 * time.Second) 284 | 285 | WithinDuration(t, a, b, 15*time.Second) 286 | 287 | mockT := new(MockT) 288 | WithinDuration(mockT, a, b, 5*time.Second) 289 | if !mockT.Failed { 290 | t.Error("Check should fail") 291 | } 292 | } 293 | 294 | func TestInDelta(t *testing.T) { 295 | t.Parallel() 296 | 297 | InDelta(t, 1.001, 1, 0.01) 298 | 299 | mockT := new(MockT) 300 | InDelta(mockT, 1, 2, 0.5) 301 | if !mockT.Failed { 302 | t.Error("Check should fail") 303 | } 304 | } 305 | 306 | func TestZero(t *testing.T) { 307 | t.Parallel() 308 | 309 | Zero(t, "") 310 | 311 | mockT := new(MockT) 312 | Zero(mockT, "x") 313 | if !mockT.Failed { 314 | t.Error("Check should fail") 315 | } 316 | } 317 | 318 | func TestNotZero(t *testing.T) { 319 | t.Parallel() 320 | 321 | NotZero(t, "x") 322 | 323 | mockT := new(MockT) 324 | NotZero(mockT, "") 325 | if !mockT.Failed { 326 | t.Error("Check should fail") 327 | } 328 | } 329 | 330 | func TestJSONEq_EqualSONString(t *testing.T) { 331 | t.Parallel() 332 | 333 | mockT := new(MockT) 334 | JSONEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) 335 | if mockT.Failed { 336 | t.Error("Check should pass") 337 | } 338 | } 339 | 340 | func TestJSONEq_EquivalentButNotEqual(t *testing.T) { 341 | t.Parallel() 342 | 343 | mockT := new(MockT) 344 | JSONEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) 345 | if mockT.Failed { 346 | t.Error("Check should pass") 347 | } 348 | } 349 | 350 | func TestJSONEq_HashOfArraysAndHashes(t *testing.T) { 351 | t.Parallel() 352 | 353 | mockT := new(MockT) 354 | 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}", 355 | "{\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}") 356 | if mockT.Failed { 357 | t.Error("Check should pass") 358 | } 359 | } 360 | 361 | func TestJSONEq_Array(t *testing.T) { 362 | t.Parallel() 363 | 364 | mockT := new(MockT) 365 | JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) 366 | if mockT.Failed { 367 | t.Error("Check should pass") 368 | } 369 | } 370 | 371 | func TestJSONEq_HashAndArrayNotEquivalent(t *testing.T) { 372 | t.Parallel() 373 | 374 | mockT := new(MockT) 375 | JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) 376 | if !mockT.Failed { 377 | t.Error("Check should fail") 378 | } 379 | } 380 | 381 | func TestJSONEq_HashesNotEquivalent(t *testing.T) { 382 | t.Parallel() 383 | 384 | mockT := new(MockT) 385 | JSONEq(mockT, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) 386 | if !mockT.Failed { 387 | t.Error("Check should fail") 388 | } 389 | } 390 | 391 | func TestJSONEq_ActualIsNotJSON(t *testing.T) { 392 | t.Parallel() 393 | 394 | mockT := new(MockT) 395 | JSONEq(mockT, `{"foo": "bar"}`, "Not JSON") 396 | if !mockT.Failed { 397 | t.Error("Check should fail") 398 | } 399 | } 400 | 401 | func TestJSONEq_ExpectedIsNotJSON(t *testing.T) { 402 | t.Parallel() 403 | 404 | mockT := new(MockT) 405 | JSONEq(mockT, "Not JSON", `{"foo": "bar", "hello": "world"}`) 406 | if !mockT.Failed { 407 | t.Error("Check should fail") 408 | } 409 | } 410 | 411 | func TestJSONEq_ExpectedAndActualNotJSON(t *testing.T) { 412 | t.Parallel() 413 | 414 | mockT := new(MockT) 415 | JSONEq(mockT, "Not JSON", "Not JSON") 416 | if !mockT.Failed { 417 | t.Error("Check should fail") 418 | } 419 | } 420 | 421 | func TestJSONEq_ArraysOfDifferentOrder(t *testing.T) { 422 | t.Parallel() 423 | 424 | mockT := new(MockT) 425 | JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) 426 | if !mockT.Failed { 427 | t.Error("Check should fail") 428 | } 429 | } 430 | 431 | func TestYAMLEq_EqualYAMLString(t *testing.T) { 432 | t.Parallel() 433 | 434 | mockT := new(MockT) 435 | YAMLEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) 436 | if mockT.Failed { 437 | t.Error("Check should pass") 438 | } 439 | } 440 | 441 | func TestYAMLEq_EquivalentButNotEqual(t *testing.T) { 442 | t.Parallel() 443 | 444 | mockT := new(MockT) 445 | YAMLEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) 446 | if mockT.Failed { 447 | t.Error("Check should pass") 448 | } 449 | } 450 | 451 | func TestYAMLEq_HashOfArraysAndHashes(t *testing.T) { 452 | t.Parallel() 453 | 454 | mockT := new(MockT) 455 | expected := ` 456 | numeric: 1.5 457 | array: 458 | - foo: bar 459 | - 1 460 | - "string" 461 | - ["nested", "array", 5.5] 462 | hash: 463 | nested: hash 464 | nested_slice: [this, is, nested] 465 | string: "foo" 466 | ` 467 | 468 | actual := ` 469 | numeric: 1.5 470 | hash: 471 | nested: hash 472 | nested_slice: [this, is, nested] 473 | string: "foo" 474 | array: 475 | - foo: bar 476 | - 1 477 | - "string" 478 | - ["nested", "array", 5.5] 479 | ` 480 | YAMLEq(mockT, expected, actual) 481 | if mockT.Failed { 482 | t.Error("Check should pass") 483 | } 484 | } 485 | 486 | func TestYAMLEq_Array(t *testing.T) { 487 | t.Parallel() 488 | 489 | mockT := new(MockT) 490 | YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) 491 | if mockT.Failed { 492 | t.Error("Check should pass") 493 | } 494 | } 495 | 496 | func TestYAMLEq_HashAndArrayNotEquivalent(t *testing.T) { 497 | t.Parallel() 498 | 499 | mockT := new(MockT) 500 | YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) 501 | if !mockT.Failed { 502 | t.Error("Check should fail") 503 | } 504 | } 505 | 506 | func TestYAMLEq_HashesNotEquivalent(t *testing.T) { 507 | t.Parallel() 508 | 509 | mockT := new(MockT) 510 | YAMLEq(mockT, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) 511 | if !mockT.Failed { 512 | t.Error("Check should fail") 513 | } 514 | } 515 | 516 | func TestYAMLEq_ActualIsSimpleString(t *testing.T) { 517 | t.Parallel() 518 | 519 | mockT := new(MockT) 520 | YAMLEq(mockT, `{"foo": "bar"}`, "Simple String") 521 | if !mockT.Failed { 522 | t.Error("Check should fail") 523 | } 524 | } 525 | 526 | func TestYAMLEq_ExpectedIsSimpleString(t *testing.T) { 527 | t.Parallel() 528 | 529 | mockT := new(MockT) 530 | YAMLEq(mockT, "Simple String", `{"foo": "bar", "hello": "world"}`) 531 | if !mockT.Failed { 532 | t.Error("Check should fail") 533 | } 534 | } 535 | 536 | func TestYAMLEq_ExpectedAndActualSimpleString(t *testing.T) { 537 | t.Parallel() 538 | 539 | mockT := new(MockT) 540 | YAMLEq(mockT, "Simple String", "Simple String") 541 | if mockT.Failed { 542 | t.Error("Check should pass") 543 | } 544 | } 545 | 546 | func TestYAMLEq_ArraysOfDifferentOrder(t *testing.T) { 547 | t.Parallel() 548 | 549 | mockT := new(MockT) 550 | YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) 551 | if !mockT.Failed { 552 | t.Error("Check should fail") 553 | } 554 | } 555 | 556 | func ExampleComparisonAssertionFunc() { 557 | t := &testing.T{} // provided by test 558 | 559 | adder := func(x, y int) int { 560 | return x + y 561 | } 562 | 563 | type args struct { 564 | x int 565 | y int 566 | } 567 | 568 | tests := []struct { 569 | name string 570 | args args 571 | expect int 572 | assertion ComparisonAssertionFunc 573 | }{ 574 | {"2+2=4", args{2, 2}, 4, Equal}, 575 | {"2+2!=5", args{2, 2}, 5, NotEqual}, 576 | {"2+3==5", args{2, 3}, 5, Exactly}, 577 | } 578 | 579 | for _, tt := range tests { 580 | t.Run(tt.name, func(t *testing.T) { 581 | tt.assertion(t, tt.expect, adder(tt.args.x, tt.args.y)) 582 | }) 583 | } 584 | } 585 | 586 | func TestComparisonAssertionFunc(t *testing.T) { 587 | t.Parallel() 588 | 589 | type iface interface { 590 | Name() string 591 | } 592 | 593 | tests := []struct { 594 | name string 595 | expect interface{} 596 | got interface{} 597 | assertion ComparisonAssertionFunc 598 | }{ 599 | {"implements", (*iface)(nil), t, Implements}, 600 | {"isType", (*testing.T)(nil), t, IsType}, 601 | {"equal", t, t, Equal}, 602 | {"equalValues", t, t, EqualValues}, 603 | {"exactly", t, t, Exactly}, 604 | {"notEqual", t, nil, NotEqual}, 605 | {"NotEqualValues", t, nil, NotEqualValues}, 606 | {"notContains", []int{1, 2, 3}, 4, NotContains}, 607 | {"subset", []int{1, 2, 3, 4}, []int{2, 3}, Subset}, 608 | {"notSubset", []int{1, 2, 3, 4}, []int{0, 3}, NotSubset}, 609 | {"elementsMatch", []byte("abc"), []byte("bac"), ElementsMatch}, 610 | {"regexp", "^t.*y$", "testify", Regexp}, 611 | {"notRegexp", "^t.*y$", "Testify", NotRegexp}, 612 | } 613 | 614 | for _, tt := range tests { 615 | t.Run(tt.name, func(t *testing.T) { 616 | tt.assertion(t, tt.expect, tt.got) 617 | }) 618 | } 619 | } 620 | 621 | func ExampleValueAssertionFunc() { 622 | t := &testing.T{} // provided by test 623 | 624 | dumbParse := func(input string) interface{} { 625 | var x interface{} 626 | json.Unmarshal([]byte(input), &x) 627 | return x 628 | } 629 | 630 | tests := []struct { 631 | name string 632 | arg string 633 | assertion ValueAssertionFunc 634 | }{ 635 | {"true is not nil", "true", NotNil}, 636 | {"empty string is nil", "", Nil}, 637 | {"zero is not nil", "0", NotNil}, 638 | {"zero is zero", "0", Zero}, 639 | {"false is zero", "false", Zero}, 640 | } 641 | 642 | for _, tt := range tests { 643 | t.Run(tt.name, func(t *testing.T) { 644 | tt.assertion(t, dumbParse(tt.arg)) 645 | }) 646 | } 647 | } 648 | 649 | func TestValueAssertionFunc(t *testing.T) { 650 | t.Parallel() 651 | 652 | tests := []struct { 653 | name string 654 | value interface{} 655 | assertion ValueAssertionFunc 656 | }{ 657 | {"notNil", true, NotNil}, 658 | {"nil", nil, Nil}, 659 | {"empty", []int{}, Empty}, 660 | {"notEmpty", []int{1}, NotEmpty}, 661 | {"zero", false, Zero}, 662 | {"notZero", 42, NotZero}, 663 | } 664 | 665 | for _, tt := range tests { 666 | t.Run(tt.name, func(t *testing.T) { 667 | tt.assertion(t, tt.value) 668 | }) 669 | } 670 | } 671 | 672 | func ExampleBoolAssertionFunc() { 673 | t := &testing.T{} // provided by test 674 | 675 | isOkay := func(x int) bool { 676 | return x >= 42 677 | } 678 | 679 | tests := []struct { 680 | name string 681 | arg int 682 | assertion BoolAssertionFunc 683 | }{ 684 | {"-1 is bad", -1, False}, 685 | {"42 is good", 42, True}, 686 | {"41 is bad", 41, False}, 687 | {"45 is cool", 45, True}, 688 | } 689 | 690 | for _, tt := range tests { 691 | t.Run(tt.name, func(t *testing.T) { 692 | tt.assertion(t, isOkay(tt.arg)) 693 | }) 694 | } 695 | } 696 | 697 | func TestBoolAssertionFunc(t *testing.T) { 698 | t.Parallel() 699 | 700 | tests := []struct { 701 | name string 702 | value bool 703 | assertion BoolAssertionFunc 704 | }{ 705 | {"true", true, True}, 706 | {"false", false, False}, 707 | } 708 | 709 | for _, tt := range tests { 710 | t.Run(tt.name, func(t *testing.T) { 711 | tt.assertion(t, tt.value) 712 | }) 713 | } 714 | } 715 | 716 | func ExampleErrorAssertionFunc() { 717 | t := &testing.T{} // provided by test 718 | 719 | dumbParseNum := func(input string, v interface{}) error { 720 | return json.Unmarshal([]byte(input), v) 721 | } 722 | 723 | tests := []struct { 724 | name string 725 | arg string 726 | assertion ErrorAssertionFunc 727 | }{ 728 | {"1.2 is number", "1.2", NoError}, 729 | {"1.2.3 not number", "1.2.3", Error}, 730 | {"true is not number", "true", Error}, 731 | {"3 is number", "3", NoError}, 732 | } 733 | 734 | for _, tt := range tests { 735 | t.Run(tt.name, func(t *testing.T) { 736 | var x float64 737 | tt.assertion(t, dumbParseNum(tt.arg, &x)) 738 | }) 739 | } 740 | } 741 | 742 | func TestErrorAssertionFunc(t *testing.T) { 743 | t.Parallel() 744 | 745 | tests := []struct { 746 | name string 747 | err error 748 | assertion ErrorAssertionFunc 749 | }{ 750 | {"noError", nil, NoError}, 751 | {"error", errors.New("whoops"), Error}, 752 | } 753 | 754 | for _, tt := range tests { 755 | t.Run(tt.name, func(t *testing.T) { 756 | tt.assertion(t, tt.err) 757 | }) 758 | } 759 | } 760 | 761 | func TestEventuallyWithTFalse(t *testing.T) { 762 | t.Parallel() 763 | 764 | mockT := new(MockT) 765 | 766 | condition := func(collect *assert.CollectT) { 767 | True(collect, false) 768 | } 769 | 770 | EventuallyWithT(mockT, condition, 100*time.Millisecond, 20*time.Millisecond) 771 | True(t, mockT.Failed, "Check should fail") 772 | } 773 | 774 | func TestEventuallyWithTTrue(t *testing.T) { 775 | t.Parallel() 776 | 777 | mockT := new(MockT) 778 | 779 | counter := 0 780 | condition := func(collect *assert.CollectT) { 781 | defer func() { 782 | counter += 1 783 | }() 784 | True(collect, counter == 1) 785 | } 786 | 787 | EventuallyWithT(mockT, condition, 100*time.Millisecond, 20*time.Millisecond) 788 | False(t, mockT.Failed, "Check should pass") 789 | Equal(t, 2, counter, "Condition is expected to be called 2 times") 790 | } 791 | -------------------------------------------------------------------------------- /suite/doc.go: -------------------------------------------------------------------------------- 1 | // Package suite contains logic for creating testing suite structs 2 | // and running the methods on those structs as tests. The most useful 3 | // piece of this package is that you can create setup/teardown methods 4 | // on your testing suites, which will run before/after the whole suite 5 | // or individual tests (depending on which interface(s) you 6 | // implement). 7 | // 8 | // The suite package does not support parallel tests. See [issue 934]. 9 | // 10 | // A testing suite is usually built by first extending the built-in 11 | // suite functionality from suite.Suite in testify. Alternatively, 12 | // you could reproduce that logic on your own if you wanted (you 13 | // just need to implement the TestingSuite interface from 14 | // suite/interfaces.go). 15 | // 16 | // After that, you can implement any of the interfaces in 17 | // suite/interfaces.go to add setup/teardown functionality to your 18 | // suite, and add any methods that start with "Test" to add tests. 19 | // Methods that do not match any suite interfaces and do not begin 20 | // with "Test" will not be run by testify, and can safely be used as 21 | // helper methods. 22 | // 23 | // Once you've built your testing suite, you need to run the suite 24 | // (using suite.Run from testify) inside any function that matches the 25 | // identity that "go test" is already looking for (i.e. 26 | // func(*testing.T)). 27 | // 28 | // Regular expression to select test suites specified command-line 29 | // argument "-run". Regular expression to select the methods 30 | // of test suites specified command-line argument "-m". 31 | // Suite object has assertion methods. 32 | // 33 | // A crude example: 34 | // 35 | // // Basic imports 36 | // import ( 37 | // "testing" 38 | // "github.com/stretchr/testify/assert" 39 | // "github.com/stretchr/testify/suite" 40 | // ) 41 | // 42 | // // Define the suite, and absorb the built-in basic suite 43 | // // functionality from testify - including a T() method which 44 | // // returns the current testing context 45 | // type ExampleTestSuite struct { 46 | // suite.Suite 47 | // VariableThatShouldStartAtFive int 48 | // } 49 | // 50 | // // Make sure that VariableThatShouldStartAtFive is set to five 51 | // // before each test 52 | // func (suite *ExampleTestSuite) SetupTest() { 53 | // suite.VariableThatShouldStartAtFive = 5 54 | // } 55 | // 56 | // // All methods that begin with "Test" are run as tests within a 57 | // // suite. 58 | // func (suite *ExampleTestSuite) TestExample() { 59 | // assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive) 60 | // suite.Equal(5, suite.VariableThatShouldStartAtFive) 61 | // } 62 | // 63 | // // In order for 'go test' to run this suite, we need to create 64 | // // a normal test function and pass our suite to suite.Run 65 | // func TestExampleTestSuite(t *testing.T) { 66 | // suite.Run(t, new(ExampleTestSuite)) 67 | // } 68 | // 69 | // [issue 934]: https://github.com/stretchr/testify/issues/934 70 | package suite 71 | -------------------------------------------------------------------------------- /suite/interfaces.go: -------------------------------------------------------------------------------- 1 | package suite 2 | 3 | import "testing" 4 | 5 | // TestingSuite can store and return the current *testing.T context 6 | // generated by 'go test'. 7 | type TestingSuite interface { 8 | T() *testing.T 9 | SetT(*testing.T) 10 | SetS(suite TestingSuite) 11 | } 12 | 13 | // SetupAllSuite has a SetupSuite method, which will run before the 14 | // tests in the suite are run. 15 | type SetupAllSuite interface { 16 | SetupSuite() 17 | } 18 | 19 | // SetupTestSuite has a SetupTest method, which will run before each 20 | // test in the suite. 21 | type SetupTestSuite interface { 22 | SetupTest() 23 | } 24 | 25 | // TearDownAllSuite has a TearDownSuite method, which will run after 26 | // all the tests in the suite have been run. 27 | type TearDownAllSuite interface { 28 | TearDownSuite() 29 | } 30 | 31 | // TearDownTestSuite has a TearDownTest method, which will run after 32 | // each test in the suite. 33 | type TearDownTestSuite interface { 34 | TearDownTest() 35 | } 36 | 37 | // BeforeTest has a function to be executed right before the test 38 | // starts and receives the suite and test names as input 39 | type BeforeTest interface { 40 | BeforeTest(suiteName, testName string) 41 | } 42 | 43 | // AfterTest has a function to be executed right after the test 44 | // finishes and receives the suite and test names as input 45 | type AfterTest interface { 46 | AfterTest(suiteName, testName string) 47 | } 48 | 49 | // WithStats implements HandleStats, a function that will be executed 50 | // when a test suite is finished. The stats contain information about 51 | // the execution of that suite and its tests. 52 | type WithStats interface { 53 | HandleStats(suiteName string, stats *SuiteInformation) 54 | } 55 | 56 | // SetupSubTest has a SetupSubTest method, which will run before each 57 | // subtest in the suite. 58 | type SetupSubTest interface { 59 | SetupSubTest() 60 | } 61 | 62 | // TearDownSubTest has a TearDownSubTest method, which will run after 63 | // each subtest in the suite have been run. 64 | type TearDownSubTest interface { 65 | TearDownSubTest() 66 | } 67 | -------------------------------------------------------------------------------- /suite/stats.go: -------------------------------------------------------------------------------- 1 | package suite 2 | 3 | import "time" 4 | 5 | // SuiteInformation stats stores stats for the whole suite execution. 6 | type SuiteInformation struct { 7 | Start, End time.Time 8 | TestStats map[string]*TestInformation 9 | } 10 | 11 | // TestInformation stores information about the execution of each test. 12 | type TestInformation struct { 13 | TestName string 14 | Start, End time.Time 15 | Passed bool 16 | } 17 | 18 | func newSuiteInformation() *SuiteInformation { 19 | testStats := make(map[string]*TestInformation) 20 | 21 | return &SuiteInformation{ 22 | TestStats: testStats, 23 | } 24 | } 25 | 26 | func (s SuiteInformation) start(testName string) { 27 | s.TestStats[testName] = &TestInformation{ 28 | TestName: testName, 29 | Start: time.Now(), 30 | } 31 | } 32 | 33 | func (s SuiteInformation) end(testName string, passed bool) { 34 | s.TestStats[testName].End = time.Now() 35 | s.TestStats[testName].Passed = passed 36 | } 37 | 38 | func (s SuiteInformation) Passed() bool { 39 | for _, stats := range s.TestStats { 40 | if !stats.Passed { 41 | return false 42 | } 43 | } 44 | 45 | return true 46 | } 47 | -------------------------------------------------------------------------------- /suite/stats_test.go: -------------------------------------------------------------------------------- 1 | package suite 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestPassedReturnsTrueWhenAllTestsPass(t *testing.T) { 10 | sinfo := newSuiteInformation() 11 | sinfo.TestStats = map[string]*TestInformation{ 12 | "Test1": {TestName: "Test1", Passed: true}, 13 | "Test2": {TestName: "Test2", Passed: true}, 14 | "Test3": {TestName: "Test3", Passed: true}, 15 | } 16 | 17 | assert.True(t, sinfo.Passed()) 18 | } 19 | 20 | func TestPassedReturnsFalseWhenSomeTestFails(t *testing.T) { 21 | sinfo := newSuiteInformation() 22 | sinfo.TestStats = map[string]*TestInformation{ 23 | "Test1": {TestName: "Test1", Passed: true}, 24 | "Test2": {TestName: "Test2", Passed: false}, 25 | "Test3": {TestName: "Test3", Passed: true}, 26 | } 27 | 28 | assert.False(t, sinfo.Passed()) 29 | } 30 | 31 | func TestPassedReturnsFalseWhenAllTestsFail(t *testing.T) { 32 | sinfo := newSuiteInformation() 33 | sinfo.TestStats = map[string]*TestInformation{ 34 | "Test1": {TestName: "Test1", Passed: false}, 35 | "Test2": {TestName: "Test2", Passed: false}, 36 | "Test3": {TestName: "Test3", Passed: false}, 37 | } 38 | 39 | assert.False(t, sinfo.Passed()) 40 | } 41 | -------------------------------------------------------------------------------- /suite/suite.go: -------------------------------------------------------------------------------- 1 | package suite 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | "reflect" 8 | "regexp" 9 | "runtime/debug" 10 | "sync" 11 | "testing" 12 | "time" 13 | 14 | "github.com/stretchr/testify/assert" 15 | "github.com/stretchr/testify/require" 16 | ) 17 | 18 | var matchMethod = flag.String("testify.m", "", "regular expression to select tests of the testify suite to run") 19 | 20 | // Suite is a basic testing suite with methods for storing and 21 | // retrieving the current *testing.T context. 22 | type Suite struct { 23 | *assert.Assertions 24 | 25 | mu sync.RWMutex 26 | require *require.Assertions 27 | t *testing.T 28 | 29 | // Parent suite to have access to the implemented methods of parent struct 30 | s TestingSuite 31 | } 32 | 33 | // T retrieves the current *testing.T context. 34 | func (suite *Suite) T() *testing.T { 35 | suite.mu.RLock() 36 | defer suite.mu.RUnlock() 37 | return suite.t 38 | } 39 | 40 | // SetT sets the current *testing.T context. 41 | func (suite *Suite) SetT(t *testing.T) { 42 | suite.mu.Lock() 43 | defer suite.mu.Unlock() 44 | suite.t = t 45 | suite.Assertions = assert.New(t) 46 | suite.require = require.New(t) 47 | } 48 | 49 | // SetS needs to set the current test suite as parent 50 | // to get access to the parent methods 51 | func (suite *Suite) SetS(s TestingSuite) { 52 | suite.s = s 53 | } 54 | 55 | // Require returns a require context for suite. 56 | func (suite *Suite) Require() *require.Assertions { 57 | suite.mu.Lock() 58 | defer suite.mu.Unlock() 59 | if suite.require == nil { 60 | panic("'Require' must not be called before 'Run' or 'SetT'") 61 | } 62 | return suite.require 63 | } 64 | 65 | // Assert returns an assert context for suite. Normally, you can call 66 | // `suite.NoError(expected, actual)`, but for situations where the embedded 67 | // methods are overridden (for example, you might want to override 68 | // assert.Assertions with require.Assertions), this method is provided so you 69 | // can call `suite.Assert().NoError()`. 70 | func (suite *Suite) Assert() *assert.Assertions { 71 | suite.mu.Lock() 72 | defer suite.mu.Unlock() 73 | if suite.Assertions == nil { 74 | panic("'Assert' must not be called before 'Run' or 'SetT'") 75 | } 76 | return suite.Assertions 77 | } 78 | 79 | func recoverAndFailOnPanic(t *testing.T) { 80 | t.Helper() 81 | r := recover() 82 | failOnPanic(t, r) 83 | } 84 | 85 | func failOnPanic(t *testing.T, r interface{}) { 86 | t.Helper() 87 | if r != nil { 88 | t.Errorf("test panicked: %v\n%s", r, debug.Stack()) 89 | t.FailNow() 90 | } 91 | } 92 | 93 | // Run provides suite functionality around golang subtests. It should be 94 | // called in place of t.Run(name, func(t *testing.T)) in test suite code. 95 | // The passed-in func will be executed as a subtest with a fresh instance of t. 96 | // Provides compatibility with go test pkg -run TestSuite/TestName/SubTestName. 97 | func (suite *Suite) Run(name string, subtest func()) bool { 98 | oldT := suite.T() 99 | 100 | return oldT.Run(name, func(t *testing.T) { 101 | suite.SetT(t) 102 | defer suite.SetT(oldT) 103 | 104 | defer recoverAndFailOnPanic(t) 105 | 106 | if setupSubTest, ok := suite.s.(SetupSubTest); ok { 107 | setupSubTest.SetupSubTest() 108 | } 109 | 110 | if tearDownSubTest, ok := suite.s.(TearDownSubTest); ok { 111 | defer tearDownSubTest.TearDownSubTest() 112 | } 113 | 114 | subtest() 115 | }) 116 | } 117 | 118 | type test = struct { 119 | name string 120 | run func(t *testing.T) 121 | } 122 | 123 | // Run takes a testing suite and runs all of the tests attached 124 | // to it. 125 | func Run(t *testing.T, suite TestingSuite) { 126 | defer recoverAndFailOnPanic(t) 127 | 128 | suite.SetT(t) 129 | suite.SetS(suite) 130 | 131 | var suiteSetupDone bool 132 | 133 | var stats *SuiteInformation 134 | if _, ok := suite.(WithStats); ok { 135 | stats = newSuiteInformation() 136 | } 137 | 138 | var tests []test 139 | methodFinder := reflect.TypeOf(suite) 140 | suiteName := methodFinder.Elem().Name() 141 | 142 | for i := 0; i < methodFinder.NumMethod(); i++ { 143 | method := methodFinder.Method(i) 144 | 145 | ok, err := methodFilter(method.Name) 146 | if err != nil { 147 | fmt.Fprintf(os.Stderr, "testify: invalid regexp for -m: %s\n", err) 148 | os.Exit(1) 149 | } 150 | 151 | if !ok { 152 | continue 153 | } 154 | 155 | if !suiteSetupDone { 156 | if stats != nil { 157 | stats.Start = time.Now() 158 | } 159 | 160 | if setupAllSuite, ok := suite.(SetupAllSuite); ok { 161 | setupAllSuite.SetupSuite() 162 | } 163 | 164 | suiteSetupDone = true 165 | } 166 | 167 | test := test{ 168 | name: method.Name, 169 | run: func(t *testing.T) { 170 | parentT := suite.T() 171 | suite.SetT(t) 172 | defer recoverAndFailOnPanic(t) 173 | defer func() { 174 | t.Helper() 175 | 176 | r := recover() 177 | 178 | if stats != nil { 179 | passed := !t.Failed() && r == nil 180 | stats.end(method.Name, passed) 181 | } 182 | 183 | if afterTestSuite, ok := suite.(AfterTest); ok { 184 | afterTestSuite.AfterTest(suiteName, method.Name) 185 | } 186 | 187 | if tearDownTestSuite, ok := suite.(TearDownTestSuite); ok { 188 | tearDownTestSuite.TearDownTest() 189 | } 190 | 191 | suite.SetT(parentT) 192 | failOnPanic(t, r) 193 | }() 194 | 195 | if setupTestSuite, ok := suite.(SetupTestSuite); ok { 196 | setupTestSuite.SetupTest() 197 | } 198 | if beforeTestSuite, ok := suite.(BeforeTest); ok { 199 | beforeTestSuite.BeforeTest(methodFinder.Elem().Name(), method.Name) 200 | } 201 | 202 | if stats != nil { 203 | stats.start(method.Name) 204 | } 205 | 206 | method.Func.Call([]reflect.Value{reflect.ValueOf(suite)}) 207 | }, 208 | } 209 | tests = append(tests, test) 210 | } 211 | if suiteSetupDone { 212 | defer func() { 213 | if tearDownAllSuite, ok := suite.(TearDownAllSuite); ok { 214 | tearDownAllSuite.TearDownSuite() 215 | } 216 | 217 | if suiteWithStats, measureStats := suite.(WithStats); measureStats { 218 | stats.End = time.Now() 219 | suiteWithStats.HandleStats(suiteName, stats) 220 | } 221 | }() 222 | } 223 | 224 | runTests(t, tests) 225 | } 226 | 227 | // Filtering method according to set regular expression 228 | // specified command-line argument -m 229 | func methodFilter(name string) (bool, error) { 230 | if ok, _ := regexp.MatchString("^Test", name); !ok { 231 | return false, nil 232 | } 233 | return regexp.MatchString(*matchMethod, name) 234 | } 235 | 236 | func runTests(t *testing.T, tests []test) { 237 | if len(tests) == 0 { 238 | t.Log("warning: no tests to run") 239 | return 240 | } 241 | 242 | for _, test := range tests { 243 | t.Run(test.name, test.run) 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /suite/suite_test.go: -------------------------------------------------------------------------------- 1 | package suite 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "flag" 7 | "io" 8 | "math/rand" 9 | "os" 10 | "os/exec" 11 | "strings" 12 | "testing" 13 | "time" 14 | 15 | "github.com/stretchr/testify/assert" 16 | "github.com/stretchr/testify/require" 17 | ) 18 | 19 | // allTestsFilter is a yes filter for testing.RunTests 20 | func allTestsFilter(pat, str string) (bool, error) { 21 | return true, nil 22 | } 23 | 24 | // SuiteRequireTwice is intended to test the usage of suite.Require in two 25 | // different tests 26 | type SuiteRequireTwice struct{ Suite } 27 | 28 | // TestSuiteRequireTwice checks for regressions of issue #149 where 29 | // suite.requirements was not initialized in suite.SetT() 30 | // A regression would result on these tests panicking rather than failing. 31 | func TestSuiteRequireTwice(t *testing.T) { 32 | ok := testing.RunTests( 33 | allTestsFilter, 34 | []testing.InternalTest{{ 35 | Name: t.Name() + "/SuiteRequireTwice", 36 | F: func(t *testing.T) { 37 | suite := new(SuiteRequireTwice) 38 | Run(t, suite) 39 | }, 40 | }}, 41 | ) 42 | assert.False(t, ok) 43 | } 44 | 45 | func (s *SuiteRequireTwice) TestRequireOne() { 46 | r := s.Require() 47 | r.Equal(1, 2) 48 | } 49 | 50 | func (s *SuiteRequireTwice) TestRequireTwo() { 51 | r := s.Require() 52 | r.Equal(1, 2) 53 | } 54 | 55 | type panickingSuite struct { 56 | Suite 57 | panicInSetupSuite bool 58 | panicInSetupTest bool 59 | panicInBeforeTest bool 60 | panicInTest bool 61 | panicInAfterTest bool 62 | panicInTearDownTest bool 63 | panicInTearDownSuite bool 64 | } 65 | 66 | func (s *panickingSuite) SetupSuite() { 67 | if s.panicInSetupSuite { 68 | panic("oops in setup suite") 69 | } 70 | } 71 | 72 | func (s *panickingSuite) SetupTest() { 73 | if s.panicInSetupTest { 74 | panic("oops in setup test") 75 | } 76 | } 77 | 78 | func (s *panickingSuite) BeforeTest(_, _ string) { 79 | if s.panicInBeforeTest { 80 | panic("oops in before test") 81 | } 82 | } 83 | 84 | func (s *panickingSuite) Test() { 85 | if s.panicInTest { 86 | panic("oops in test") 87 | } 88 | } 89 | 90 | func (s *panickingSuite) AfterTest(_, _ string) { 91 | if s.panicInAfterTest { 92 | panic("oops in after test") 93 | } 94 | } 95 | 96 | func (s *panickingSuite) TearDownTest() { 97 | if s.panicInTearDownTest { 98 | panic("oops in tear down test") 99 | } 100 | } 101 | 102 | func (s *panickingSuite) TearDownSuite() { 103 | if s.panicInTearDownSuite { 104 | panic("oops in tear down suite") 105 | } 106 | } 107 | 108 | func TestSuiteRecoverPanic(t *testing.T) { 109 | ok := true 110 | panickingTests := []testing.InternalTest{ 111 | { 112 | Name: t.Name() + "/InSetupSuite", 113 | F: func(t *testing.T) { Run(t, &panickingSuite{panicInSetupSuite: true}) }, 114 | }, 115 | { 116 | Name: t.Name() + "/InSetupTest", 117 | F: func(t *testing.T) { Run(t, &panickingSuite{panicInSetupTest: true}) }, 118 | }, 119 | { 120 | Name: t.Name() + "InBeforeTest", 121 | F: func(t *testing.T) { Run(t, &panickingSuite{panicInBeforeTest: true}) }, 122 | }, 123 | { 124 | Name: t.Name() + "/InTest", 125 | F: func(t *testing.T) { Run(t, &panickingSuite{panicInTest: true}) }, 126 | }, 127 | { 128 | Name: t.Name() + "/InAfterTest", 129 | F: func(t *testing.T) { Run(t, &panickingSuite{panicInAfterTest: true}) }, 130 | }, 131 | { 132 | Name: t.Name() + "/InTearDownTest", 133 | F: func(t *testing.T) { Run(t, &panickingSuite{panicInTearDownTest: true}) }, 134 | }, 135 | { 136 | Name: t.Name() + "/InTearDownSuite", 137 | F: func(t *testing.T) { Run(t, &panickingSuite{panicInTearDownSuite: true}) }, 138 | }, 139 | } 140 | 141 | require.NotPanics(t, func() { 142 | ok = testing.RunTests(allTestsFilter, panickingTests) 143 | }) 144 | 145 | assert.False(t, ok) 146 | } 147 | 148 | // This suite is intended to store values to make sure that only 149 | // testing-suite-related methods are run. It's also a fully 150 | // functional example of a testing suite, using setup/teardown methods 151 | // and a helper method that is ignored by testify. To make this look 152 | // more like a real world example, all tests in the suite perform some 153 | // type of assertion. 154 | type SuiteTester struct { 155 | // Include our basic suite logic. 156 | Suite 157 | 158 | // Keep counts of how many times each method is run. 159 | SetupSuiteRunCount int 160 | TearDownSuiteRunCount int 161 | SetupTestRunCount int 162 | TearDownTestRunCount int 163 | TestOneRunCount int 164 | TestTwoRunCount int 165 | TestSubtestRunCount int 166 | NonTestMethodRunCount int 167 | SetupSubTestRunCount int 168 | TearDownSubTestRunCount int 169 | 170 | SetupSubTestNames []string 171 | TearDownSubTestNames []string 172 | 173 | SuiteNameBefore []string 174 | TestNameBefore []string 175 | 176 | SuiteNameAfter []string 177 | TestNameAfter []string 178 | 179 | TimeBefore []time.Time 180 | TimeAfter []time.Time 181 | } 182 | 183 | // The SetupSuite method will be run by testify once, at the very 184 | // start of the testing suite, before any tests are run. 185 | func (suite *SuiteTester) SetupSuite() { 186 | suite.SetupSuiteRunCount++ 187 | } 188 | 189 | func (suite *SuiteTester) BeforeTest(suiteName, testName string) { 190 | suite.SuiteNameBefore = append(suite.SuiteNameBefore, suiteName) 191 | suite.TestNameBefore = append(suite.TestNameBefore, testName) 192 | suite.TimeBefore = append(suite.TimeBefore, time.Now()) 193 | } 194 | 195 | func (suite *SuiteTester) AfterTest(suiteName, testName string) { 196 | suite.SuiteNameAfter = append(suite.SuiteNameAfter, suiteName) 197 | suite.TestNameAfter = append(suite.TestNameAfter, testName) 198 | suite.TimeAfter = append(suite.TimeAfter, time.Now()) 199 | } 200 | 201 | // The TearDownSuite method will be run by testify once, at the very 202 | // end of the testing suite, after all tests have been run. 203 | func (suite *SuiteTester) TearDownSuite() { 204 | suite.TearDownSuiteRunCount++ 205 | } 206 | 207 | // The SetupTest method will be run before every test in the suite. 208 | func (suite *SuiteTester) SetupTest() { 209 | suite.SetupTestRunCount++ 210 | } 211 | 212 | // The TearDownTest method will be run after every test in the suite. 213 | func (suite *SuiteTester) TearDownTest() { 214 | suite.TearDownTestRunCount++ 215 | } 216 | 217 | // Every method in a testing suite that begins with "Test" will be run 218 | // as a test. TestOne is an example of a test. For the purposes of 219 | // this example, we've included assertions in the tests, since most 220 | // tests will issue assertions. 221 | func (suite *SuiteTester) TestOne() { 222 | beforeCount := suite.TestOneRunCount 223 | suite.TestOneRunCount++ 224 | assert.Equal(suite.T(), suite.TestOneRunCount, beforeCount+1) 225 | suite.Equal(suite.TestOneRunCount, beforeCount+1) 226 | } 227 | 228 | // TestTwo is another example of a test. 229 | func (suite *SuiteTester) TestTwo() { 230 | beforeCount := suite.TestTwoRunCount 231 | suite.TestTwoRunCount++ 232 | assert.NotEqual(suite.T(), suite.TestTwoRunCount, beforeCount) 233 | suite.NotEqual(suite.TestTwoRunCount, beforeCount) 234 | } 235 | 236 | func (suite *SuiteTester) TestSkip() { 237 | suite.T().Skip() 238 | } 239 | 240 | // NonTestMethod does not begin with "Test", so it will not be run by 241 | // testify as a test in the suite. This is useful for creating helper 242 | // methods for your tests. 243 | func (suite *SuiteTester) NonTestMethod() { 244 | suite.NonTestMethodRunCount++ 245 | } 246 | 247 | func (suite *SuiteTester) TestSubtest() { 248 | suite.TestSubtestRunCount++ 249 | 250 | for _, t := range []struct { 251 | testName string 252 | }{ 253 | {"first"}, 254 | {"second"}, 255 | } { 256 | suiteT := suite.T() 257 | suite.Run(t.testName, func() { 258 | // We should get a different *testing.T for subtests, so that 259 | // go test recognizes them as proper subtests for output formatting 260 | // and running individual subtests 261 | subTestT := suite.T() 262 | suite.NotEqual(subTestT, suiteT) 263 | }) 264 | suite.Equal(suiteT, suite.T()) 265 | } 266 | } 267 | 268 | func (suite *SuiteTester) TearDownSubTest() { 269 | suite.TearDownSubTestNames = append(suite.TearDownSubTestNames, suite.T().Name()) 270 | suite.TearDownSubTestRunCount++ 271 | } 272 | 273 | func (suite *SuiteTester) SetupSubTest() { 274 | suite.SetupSubTestNames = append(suite.SetupSubTestNames, suite.T().Name()) 275 | suite.SetupSubTestRunCount++ 276 | } 277 | 278 | type SuiteSkipTester struct { 279 | // Include our basic suite logic. 280 | Suite 281 | 282 | // Keep counts of how many times each method is run. 283 | SetupSuiteRunCount int 284 | TearDownSuiteRunCount int 285 | } 286 | 287 | func (suite *SuiteSkipTester) SetupSuite() { 288 | suite.SetupSuiteRunCount++ 289 | suite.T().Skip() 290 | } 291 | 292 | func (suite *SuiteSkipTester) TestNothing() { 293 | // SetupSuite is only called when at least one test satisfies 294 | // test filter. For this suite to be set up (and then tore down) 295 | // it is necessary to add at least one test method. 296 | } 297 | 298 | func (suite *SuiteSkipTester) TearDownSuite() { 299 | suite.TearDownSuiteRunCount++ 300 | } 301 | 302 | // TestRunSuite will be run by the 'go test' command, so within it, we 303 | // can run our suite using the Run(*testing.T, TestingSuite) function. 304 | func TestRunSuite(t *testing.T) { 305 | suiteTester := new(SuiteTester) 306 | Run(t, suiteTester) 307 | 308 | // Normally, the test would end here. The following are simply 309 | // some assertions to ensure that the Run function is working as 310 | // intended - they are not part of the example. 311 | 312 | // The suite was only run once, so the SetupSuite and TearDownSuite 313 | // methods should have each been run only once. 314 | assert.Equal(t, 1, suiteTester.SetupSuiteRunCount) 315 | assert.Equal(t, 1, suiteTester.TearDownSuiteRunCount) 316 | 317 | assert.Len(t, suiteTester.SuiteNameAfter, 4) 318 | assert.Len(t, suiteTester.SuiteNameBefore, 4) 319 | assert.Len(t, suiteTester.TestNameAfter, 4) 320 | assert.Len(t, suiteTester.TestNameBefore, 4) 321 | 322 | assert.Contains(t, suiteTester.TestNameAfter, "TestOne") 323 | assert.Contains(t, suiteTester.TestNameAfter, "TestTwo") 324 | assert.Contains(t, suiteTester.TestNameAfter, "TestSkip") 325 | assert.Contains(t, suiteTester.TestNameAfter, "TestSubtest") 326 | 327 | assert.Contains(t, suiteTester.TestNameBefore, "TestOne") 328 | assert.Contains(t, suiteTester.TestNameBefore, "TestTwo") 329 | assert.Contains(t, suiteTester.TestNameBefore, "TestSkip") 330 | assert.Contains(t, suiteTester.TestNameBefore, "TestSubtest") 331 | 332 | assert.Contains(t, suiteTester.SetupSubTestNames, "TestRunSuite/TestSubtest/first") 333 | assert.Contains(t, suiteTester.SetupSubTestNames, "TestRunSuite/TestSubtest/second") 334 | 335 | assert.Contains(t, suiteTester.TearDownSubTestNames, "TestRunSuite/TestSubtest/first") 336 | assert.Contains(t, suiteTester.TearDownSubTestNames, "TestRunSuite/TestSubtest/second") 337 | 338 | for _, suiteName := range suiteTester.SuiteNameAfter { 339 | assert.Equal(t, "SuiteTester", suiteName) 340 | } 341 | 342 | for _, suiteName := range suiteTester.SuiteNameBefore { 343 | assert.Equal(t, "SuiteTester", suiteName) 344 | } 345 | 346 | for _, when := range suiteTester.TimeAfter { 347 | assert.False(t, when.IsZero()) 348 | } 349 | 350 | for _, when := range suiteTester.TimeBefore { 351 | assert.False(t, when.IsZero()) 352 | } 353 | 354 | // There are four test methods (TestOne, TestTwo, TestSkip, and TestSubtest), so 355 | // the SetupTest and TearDownTest methods (which should be run once for 356 | // each test) should have been run four times. 357 | assert.Equal(t, 4, suiteTester.SetupTestRunCount) 358 | assert.Equal(t, 4, suiteTester.TearDownTestRunCount) 359 | 360 | // Each test should have been run once. 361 | assert.Equal(t, 1, suiteTester.TestOneRunCount) 362 | assert.Equal(t, 1, suiteTester.TestTwoRunCount) 363 | assert.Equal(t, 1, suiteTester.TestSubtestRunCount) 364 | 365 | assert.Equal(t, 2, suiteTester.TearDownSubTestRunCount) 366 | assert.Equal(t, 2, suiteTester.SetupSubTestRunCount) 367 | 368 | // Methods that don't match the test method identifier shouldn't 369 | // have been run at all. 370 | assert.Equal(t, 0, suiteTester.NonTestMethodRunCount) 371 | 372 | suiteSkipTester := new(SuiteSkipTester) 373 | Run(t, suiteSkipTester) 374 | 375 | // The suite was only run once, so the SetupSuite and TearDownSuite 376 | // methods should have each been run only once, even though SetupSuite 377 | // called Skip() 378 | assert.Equal(t, 1, suiteSkipTester.SetupSuiteRunCount) 379 | assert.Equal(t, 1, suiteSkipTester.TearDownSuiteRunCount) 380 | 381 | } 382 | 383 | // This suite has no Test... methods. It's setup and teardown must be skipped. 384 | type SuiteSetupSkipTester struct { 385 | Suite 386 | 387 | setUp bool 388 | toreDown bool 389 | } 390 | 391 | func (s *SuiteSetupSkipTester) SetupSuite() { 392 | s.setUp = true 393 | } 394 | 395 | func (s *SuiteSetupSkipTester) NonTestMethod() { 396 | 397 | } 398 | 399 | func (s *SuiteSetupSkipTester) TearDownSuite() { 400 | s.toreDown = true 401 | } 402 | 403 | func TestSkippingSuiteSetup(t *testing.T) { 404 | suiteTester := new(SuiteSetupSkipTester) 405 | Run(t, suiteTester) 406 | assert.False(t, suiteTester.setUp) 407 | assert.False(t, suiteTester.toreDown) 408 | } 409 | 410 | func TestSuiteGetters(t *testing.T) { 411 | suite := new(SuiteTester) 412 | suite.SetT(t) 413 | assert.NotNil(t, suite.Assert()) 414 | assert.Equal(t, suite.Assertions, suite.Assert()) 415 | assert.NotNil(t, suite.Require()) 416 | assert.Equal(t, suite.require, suite.Require()) 417 | } 418 | 419 | type SuiteLoggingTester struct { 420 | Suite 421 | } 422 | 423 | func (s *SuiteLoggingTester) TestLoggingPass() { 424 | s.T().Log("TESTLOGPASS") 425 | } 426 | 427 | func (s *SuiteLoggingTester) TestLoggingFail() { 428 | s.T().Log("TESTLOGFAIL") 429 | assert.NotNil(s.T(), nil) // expected to fail 430 | } 431 | 432 | type StdoutCapture struct { 433 | oldStdout *os.File 434 | readPipe *os.File 435 | } 436 | 437 | func (sc *StdoutCapture) StartCapture() { 438 | sc.oldStdout = os.Stdout 439 | sc.readPipe, os.Stdout, _ = os.Pipe() 440 | } 441 | 442 | func (sc *StdoutCapture) StopCapture() (string, error) { 443 | if sc.oldStdout == nil || sc.readPipe == nil { 444 | return "", errors.New("StartCapture not called before StopCapture") 445 | } 446 | os.Stdout.Close() 447 | os.Stdout = sc.oldStdout 448 | bytes, err := io.ReadAll(sc.readPipe) 449 | if err != nil { 450 | return "", err 451 | } 452 | return string(bytes), nil 453 | } 454 | 455 | func TestSuiteLogging(t *testing.T) { 456 | suiteLoggingTester := new(SuiteLoggingTester) 457 | capture := StdoutCapture{} 458 | internalTest := testing.InternalTest{ 459 | Name: t.Name() + "/SuiteLoggingTester", 460 | F: func(subT *testing.T) { 461 | Run(subT, suiteLoggingTester) 462 | }, 463 | } 464 | capture.StartCapture() 465 | testing.RunTests(allTestsFilter, []testing.InternalTest{internalTest}) 466 | output, err := capture.StopCapture() 467 | require.NoError(t, err, "Got an error trying to capture stdout and stderr!") 468 | require.NotEmpty(t, output, "output content must not be empty") 469 | 470 | // Failed tests' output is always printed 471 | assert.Contains(t, output, "TESTLOGFAIL") 472 | 473 | if testing.Verbose() { 474 | // In verbose mode, output from successful tests is also printed 475 | assert.Contains(t, output, "TESTLOGPASS") 476 | } else { 477 | assert.NotContains(t, output, "TESTLOGPASS") 478 | } 479 | } 480 | 481 | type CallOrderSuite struct { 482 | Suite 483 | callOrder []string 484 | } 485 | 486 | func (s *CallOrderSuite) call(method string) { 487 | time.Sleep(time.Duration(rand.Intn(300)) * time.Millisecond) 488 | s.callOrder = append(s.callOrder, method) 489 | } 490 | 491 | func TestSuiteCallOrder(t *testing.T) { 492 | Run(t, new(CallOrderSuite)) 493 | } 494 | func (s *CallOrderSuite) SetupSuite() { 495 | s.call("SetupSuite") 496 | } 497 | 498 | func (s *CallOrderSuite) TearDownSuite() { 499 | s.call("TearDownSuite") 500 | assert.Equal(s.T(), "SetupSuite;SetupTest;Test A;SetupSubTest;SubTest A1;TearDownSubTest;SetupSubTest;SubTest A2;TearDownSubTest;TearDownTest;SetupTest;Test B;SetupSubTest;SubTest B1;TearDownSubTest;SetupSubTest;SubTest B2;TearDownSubTest;TearDownTest;TearDownSuite", strings.Join(s.callOrder, ";")) 501 | } 502 | func (s *CallOrderSuite) SetupTest() { 503 | s.call("SetupTest") 504 | } 505 | 506 | func (s *CallOrderSuite) TearDownTest() { 507 | s.call("TearDownTest") 508 | } 509 | 510 | func (s *CallOrderSuite) SetupSubTest() { 511 | s.call("SetupSubTest") 512 | } 513 | 514 | func (s *CallOrderSuite) TearDownSubTest() { 515 | s.call("TearDownSubTest") 516 | } 517 | 518 | func (s *CallOrderSuite) Test_A() { 519 | s.call("Test A") 520 | s.Run("SubTest A1", func() { 521 | s.call("SubTest A1") 522 | }) 523 | s.Run("SubTest A2", func() { 524 | s.call("SubTest A2") 525 | }) 526 | } 527 | 528 | func (s *CallOrderSuite) Test_B() { 529 | s.call("Test B") 530 | s.Run("SubTest B1", func() { 531 | s.call("SubTest B1") 532 | }) 533 | s.Run("SubTest B2", func() { 534 | s.call("SubTest B2") 535 | }) 536 | } 537 | 538 | type suiteWithStats struct { 539 | Suite 540 | wasCalled bool 541 | stats *SuiteInformation 542 | } 543 | 544 | func (s *suiteWithStats) HandleStats(suiteName string, stats *SuiteInformation) { 545 | s.wasCalled = true 546 | s.stats = stats 547 | } 548 | 549 | func (s *suiteWithStats) TestSomething() { 550 | s.Equal(1, 1) 551 | } 552 | 553 | func (s *suiteWithStats) TestPanic() { 554 | panic("oops") 555 | } 556 | 557 | func TestSuiteWithStats(t *testing.T) { 558 | suiteWithStats := new(suiteWithStats) 559 | 560 | suiteSuccess := testing.RunTests(allTestsFilter, []testing.InternalTest{ 561 | { 562 | Name: t.Name() + "/suiteWithStats", 563 | F: func(t *testing.T) { 564 | Run(t, suiteWithStats) 565 | }, 566 | }, 567 | }) 568 | require.False(t, suiteSuccess, "suiteWithStats should report test failure because of panic in TestPanic") 569 | 570 | assert.True(t, suiteWithStats.wasCalled) 571 | assert.NotZero(t, suiteWithStats.stats.Start) 572 | assert.NotZero(t, suiteWithStats.stats.End) 573 | assert.False(t, suiteWithStats.stats.Passed()) 574 | 575 | testStats := suiteWithStats.stats.TestStats 576 | 577 | assert.NotZero(t, testStats["TestSomething"].Start) 578 | assert.NotZero(t, testStats["TestSomething"].End) 579 | assert.True(t, testStats["TestSomething"].Passed) 580 | 581 | assert.NotZero(t, testStats["TestPanic"].Start) 582 | assert.NotZero(t, testStats["TestPanic"].End) 583 | assert.False(t, testStats["TestPanic"].Passed) 584 | } 585 | 586 | // FailfastSuite will test the behavior when running with the failfast flag 587 | // It logs calls in the callOrder slice which we then use to assert the correct calls were made 588 | type FailfastSuite struct { 589 | Suite 590 | callOrder []string 591 | } 592 | 593 | func (s *FailfastSuite) call(method string) { 594 | s.callOrder = append(s.callOrder, method) 595 | } 596 | 597 | func TestFailfastSuite(t *testing.T) { 598 | // This test suite is run twice. Once normally and once with the -failfast flag by TestFailfastSuiteFailFastOn 599 | // If you need to debug it run this test directly with the failfast flag set on/off as you need 600 | failFast := flag.Lookup("test.failfast").Value.(flag.Getter).Get().(bool) 601 | s := new(FailfastSuite) 602 | ok := testing.RunTests( 603 | allTestsFilter, 604 | []testing.InternalTest{{ 605 | Name: t.Name() + "/FailfastSuite", 606 | F: func(t *testing.T) { 607 | Run(t, s) 608 | }, 609 | }}, 610 | ) 611 | assert.False(t, ok) 612 | var expect []string 613 | if failFast { 614 | // Test A Fails and because we are running with failfast Test B never runs and we proceed straight to TearDownSuite 615 | expect = []string{"SetupSuite", "SetupTest", "Test A Fails", "TearDownTest", "TearDownSuite"} 616 | } else { 617 | // Test A Fails and because we are running without failfast we continue and run Test B and then proceed to TearDownSuite 618 | expect = []string{"SetupSuite", "SetupTest", "Test A Fails", "TearDownTest", "SetupTest", "Test B Passes", "TearDownTest", "TearDownSuite"} 619 | } 620 | callOrderAssert(t, expect, s.callOrder) 621 | } 622 | 623 | type tHelper interface { 624 | Helper() 625 | } 626 | 627 | // callOrderAssert is a help with confirms that asserts that expect 628 | // matches one or more times in callOrder. This makes it compatible 629 | // with go test flag -count=X where X > 1. 630 | func callOrderAssert(t *testing.T, expect, callOrder []string) { 631 | var ti interface{} = t 632 | if h, ok := ti.(tHelper); ok { 633 | h.Helper() 634 | } 635 | 636 | callCount := len(callOrder) 637 | expectCount := len(expect) 638 | if callCount > expectCount && callCount%expectCount == 0 { 639 | // Command line flag -count=X where X > 1. 640 | for len(callOrder) >= expectCount { 641 | assert.Equal(t, expect, callOrder[:expectCount]) 642 | callOrder = callOrder[expectCount:] 643 | } 644 | return 645 | } 646 | 647 | assert.Equal(t, expect, callOrder) 648 | } 649 | 650 | func TestFailfastSuiteFailFastOn(t *testing.T) { 651 | // To test this with failfast on (and isolated from other intended test failures in our test suite) we launch it in its own process 652 | cmd := exec.Command("go", "test", "-v", "-race", "-run", "TestFailfastSuite", "-failfast") 653 | var out bytes.Buffer 654 | cmd.Stdout = &out 655 | t.Log("Running go test -v -race -run TestFailfastSuite -failfast") 656 | err := cmd.Run() 657 | t.Log(out.String()) 658 | if err != nil { 659 | t.Log(err) 660 | t.Fail() 661 | } 662 | } 663 | func (s *FailfastSuite) SetupSuite() { 664 | s.call("SetupSuite") 665 | } 666 | 667 | func (s *FailfastSuite) TearDownSuite() { 668 | s.call("TearDownSuite") 669 | } 670 | func (s *FailfastSuite) SetupTest() { 671 | s.call("SetupTest") 672 | } 673 | 674 | func (s *FailfastSuite) TearDownTest() { 675 | s.call("TearDownTest") 676 | } 677 | 678 | func (s *FailfastSuite) Test_A_Fails() { 679 | s.call("Test A Fails") 680 | s.T().Error("Test A meant to fail") 681 | } 682 | 683 | func (s *FailfastSuite) Test_B_Passes() { 684 | s.call("Test B Passes") 685 | s.Require().True(true) 686 | } 687 | 688 | type subtestPanicSuite struct { 689 | Suite 690 | inTearDownSuite bool 691 | inTearDownTest bool 692 | inTearDownSubTest bool 693 | } 694 | 695 | func (s *subtestPanicSuite) TearDownSuite() { 696 | s.inTearDownSuite = true 697 | } 698 | 699 | func (s *subtestPanicSuite) TearDownTest() { 700 | s.inTearDownTest = true 701 | } 702 | 703 | func (s *subtestPanicSuite) TearDownSubTest() { 704 | s.inTearDownSubTest = true 705 | } 706 | 707 | func (s *subtestPanicSuite) TestSubtestPanic() { 708 | ok := s.Run("subtest", func() { 709 | panic("panic") 710 | }) 711 | s.False(ok, "subtest failure is expected") 712 | } 713 | 714 | func TestSubtestPanic(t *testing.T) { 715 | suite := new(subtestPanicSuite) 716 | ok := testing.RunTests( 717 | allTestsFilter, 718 | []testing.InternalTest{{ 719 | Name: t.Name() + "/subtestPanicSuite", 720 | F: func(t *testing.T) { 721 | Run(t, suite) 722 | }, 723 | }}, 724 | ) 725 | assert.False(t, ok, "TestSubtestPanic/subtest should make the testsuite fail") 726 | assert.True(t, suite.inTearDownSubTest) 727 | assert.True(t, suite.inTearDownTest) 728 | assert.True(t, suite.inTearDownSuite) 729 | } 730 | 731 | type unInitializedSuite struct { 732 | Suite 733 | } 734 | 735 | // TestUnInitializedSuites asserts the behavior of the suite methods when the 736 | // suite is not initialized 737 | func TestUnInitializedSuites(t *testing.T) { 738 | t.Run("should panic on Require", func(t *testing.T) { 739 | suite := new(unInitializedSuite) 740 | 741 | assert.Panics(t, func() { 742 | suite.Require().True(true) 743 | }) 744 | }) 745 | 746 | t.Run("should panic on Assert", func(t *testing.T) { 747 | suite := new(unInitializedSuite) 748 | 749 | assert.Panics(t, func() { 750 | suite.Assert().True(true) 751 | }) 752 | }) 753 | } 754 | --------------------------------------------------------------------------------