From e6d938c245dc933a0bd2d2d85a164ebfaa1cc5ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Fri, 30 May 2025 10:20:00 +0200 Subject: [PATCH 1/4] mock: isolate objx-dependent code in their own source files In order to ease maintenance of downstream forks of Testify that would remove dependency on github.com/stretchr/objx we move all uses of that module in isolated source files. A fork without that dependency could just remove those files. See https://github.com/stretchr/testify/issues/1752#issuecomment-2921545929 The use of objx is quite contained: it is only used in Mock.TestData(). Note that we can't just remove that method or change the return value because that would be a breaking change. --- mock/mock.go | 14 +------------- mock/mock_objx.go | 19 +++++++++++++++++++ mock/mock_objx_test.go | 19 +++++++++++++++++++ mock/mock_test.go | 12 ------------ 4 files changed, 39 insertions(+), 25 deletions(-) create mode 100644 mock/mock_objx.go create mode 100644 mock/mock_objx_test.go diff --git a/mock/mock.go b/mock/mock.go index a13c37f3b..f36641fca 100644 --- a/mock/mock.go +++ b/mock/mock.go @@ -11,8 +11,6 @@ import ( "sync" "time" - "github.com/stretchr/objx" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/internal/difflib" "github.com/stretchr/testify/internal/spew" @@ -311,7 +309,7 @@ type Mock struct { // TestData holds any data that might be useful for testing. Testify ignores // this data completely allowing you to do whatever you like with it. - testData objx.Map + testData testData mutex sync.Mutex } @@ -324,16 +322,6 @@ func (m *Mock) String() string { return fmt.Sprintf("%[1]T<%[1]p>", m) } -// TestData holds any data that might be useful for testing. Testify ignores -// this data completely allowing you to do whatever you like with it. -func (m *Mock) TestData() objx.Map { - if m.testData == nil { - m.testData = make(objx.Map) - } - - return m.testData -} - /* Setting expectations */ diff --git a/mock/mock_objx.go b/mock/mock_objx.go new file mode 100644 index 000000000..37fbdbf6e --- /dev/null +++ b/mock/mock_objx.go @@ -0,0 +1,19 @@ +// This source file isolates the uses of the objx module to ease +// maintenance of downstream forks that remove that dependency. +// See https://github.com/stretchr/testify/issues/1752 + +package mock + +import "github.com/stretchr/objx" + +type testData = objx.Map + +// TestData holds any data that might be useful for testing. Testify ignores +// this data completely allowing you to do whatever you like with it. +func (m *Mock) TestData() objx.Map { + if m.testData == nil { + m.testData = make(objx.Map) + } + + return m.testData +} diff --git a/mock/mock_objx_test.go b/mock/mock_objx_test.go new file mode 100644 index 000000000..37adf43ae --- /dev/null +++ b/mock/mock_objx_test.go @@ -0,0 +1,19 @@ +package mock + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_Mock_TestData(t *testing.T) { + t.Parallel() + + var mockedService = new(TestExampleImplementation) + + if assert.NotNil(t, mockedService.TestData()) { + + mockedService.TestData().Set("something", 123) + assert.Equal(t, 123, mockedService.TestData().Get("something").Data()) + } +} diff --git a/mock/mock_test.go b/mock/mock_test.go index 3dc9e0b1e..698e0f5ac 100644 --- a/mock/mock_test.go +++ b/mock/mock_test.go @@ -164,18 +164,6 @@ func (m *MockTestingT) FailNow() { Mock */ -func Test_Mock_TestData(t *testing.T) { - t.Parallel() - - var mockedService = new(TestExampleImplementation) - - if assert.NotNil(t, mockedService.TestData()) { - - mockedService.TestData().Set("something", 123) - assert.Equal(t, 123, mockedService.TestData().Get("something").Data()) - } -} - func Test_Mock_On(t *testing.T) { t.Parallel() From 5ed82c54f55e0e02e5ae33e3459bfc9665c6db80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Fri, 13 Jun 2025 23:26:42 +0200 Subject: [PATCH 2/4] mock: add build tag 'testify_no_objx' to opt-out of objx Add build tag testify_no_objx to allow to exclude method Mock.TestData() for avoiding dependency on github.com/stretchr/objx. --- mock/mock_no_objx.go | 5 +++++ mock/mock_objx.go | 2 ++ mock/mock_objx_test.go | 2 ++ 3 files changed, 9 insertions(+) create mode 100644 mock/mock_no_objx.go diff --git a/mock/mock_no_objx.go b/mock/mock_no_objx.go new file mode 100644 index 000000000..e8db50ec0 --- /dev/null +++ b/mock/mock_no_objx.go @@ -0,0 +1,5 @@ +//go:build testify_no_objx || testify_no_deps + +package mock + +type testData = struct{} diff --git a/mock/mock_objx.go b/mock/mock_objx.go index 37fbdbf6e..77f75dd65 100644 --- a/mock/mock_objx.go +++ b/mock/mock_objx.go @@ -2,6 +2,8 @@ // maintenance of downstream forks that remove that dependency. // See https://github.com/stretchr/testify/issues/1752 +//go:build !testify_no_objx && !testify_no_deps + package mock import "github.com/stretchr/objx" diff --git a/mock/mock_objx_test.go b/mock/mock_objx_test.go index 37adf43ae..556ac9c44 100644 --- a/mock/mock_objx_test.go +++ b/mock/mock_objx_test.go @@ -1,3 +1,5 @@ +//go:build !testify_no_objx && !testify_no_deps + package mock import ( From 3196432b2c12c67506376e488159df2680c6716e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Fri, 13 Jun 2025 23:34:19 +0200 Subject: [PATCH 3/4] CI: also run tests with build tag 'testify_no_objx' --- .github/workflows/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6b40f0c98..c2a2dd3b3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,6 +21,7 @@ jobs: - run: ./.ci.readme.fmt.sh - run: ./.ci.govet.sh - run: go test -v -race ./... + - run: go test -tags testify_no_objx ./... test: runs-on: ubuntu-latest strategy: @@ -41,3 +42,4 @@ jobs: with: go-version: ${{ matrix.go_version }} - run: go test -v -race ./... + - run: go test -tags testify_no_objx ./... From aa3233d0382d9a24f162662be70cfdc2953ed6d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Wed, 8 Apr 2026 19:00:37 +0200 Subject: [PATCH 4/4] mock: drop objx.Map: "Mock.TestData() objx.Map" now returns TestData Drop the last reference to dependency github.com/stretchr/objx. Reference to objx.Map is removed (BREAKING CHANGE) from the Mock.TestData method signature. A similar object is returned instead of objx.Map, but we do not aim to implement the full objx.Value API: instead we expose the reflect.Value API that should help enough the few users of Mock.TestData to move away from objx.Map reliance. Implemented as planned in #1852. --- go.mod | 5 +---- go.sum | 4 ---- mock/mock_objx.go | 55 +++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 7f6d2a85e..b624c59e3 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,7 @@ module github.com/stretchr/testify // .github/workflows/main.yml go 1.17 -require ( - github.com/stretchr/objx v0.5.2 // To avoid a cycle the version of testify used by objx should be excluded below - gopkg.in/yaml.v3 v3.0.1 -) +require gopkg.in/yaml.v3 v3.0.1 // Break dependency cycle with objx. // See https://github.com/stretchr/objx/pull/140 diff --git a/go.sum b/go.sum index 2f74e66dd..a62c313c5 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,3 @@ -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/mock/mock_objx.go b/mock/mock_objx.go index 77f75dd65..7d7e0eda5 100644 --- a/mock/mock_objx.go +++ b/mock/mock_objx.go @@ -6,16 +6,63 @@ package mock -import "github.com/stretchr/objx" +import ( + "reflect" +) -type testData = objx.Map +type testData = TestData // TestData holds any data that might be useful for testing. Testify ignores // this data completely allowing you to do whatever you like with it. -func (m *Mock) TestData() objx.Map { +// +// Deprecated: do not use. The new TestData do not return an [github.com/stretchr/objx.Map] anymore. +// See https://github.com/stretchr/testify/issues/1852. +func (m *Mock) TestData() TestData { if m.testData == nil { - m.testData = make(objx.Map) + m.testData = make(TestData) } return m.testData } + +// TestData replaces [github.com/stretchr/objx.Map]. +type TestData map[string]interface{} + +type reflectValue = reflect.Value + +// TestDataValue replaces [github.com/stretchr/objx.Value] and exposes the same methods as [reflect.Value]. +// Only a subset of objx.Value methods are available. +type TestDataValue struct { + reflectValue +} + +// Set replaces [github.com/stretchr/objx.Map.Set]. +func (td TestData) Set(selector string, v interface{}) { + td[selector] = v +} + +// Get replaces [github.com/stretchr/objx.Map.Get]. +func (td TestData) Get(selector string) *TestDataValue { + v, ok := td[selector] + if !ok { + return nil + } + return &TestDataValue{reflectValue: reflect.ValueOf(&v).Elem()} +} + +// MustInter replaces [github.com/stretchr/objx.Value.MustInter]. +func (v *TestDataValue) MustInter() interface{} { + if v == nil { + return nil + } + // v.reflectValue contains an interface (ex: error), so dereference it + return v.reflectValue.Elem() +} + +// MustInter replaces [github.com/stretchr/objx.Value.Data]. +func (v *TestDataValue) Data() interface{} { + if v == nil { + return nil + } + return v.reflectValue.Interface() +}