Skip to content

Commit ca0f3dc

Browse files
authored
feat: added migration tool to transition from v1 to v2 (#61)
* feat: added migration tool to transition from v1 to v2 Signed-off-by: Frederic BIDON <fredbi@yahoo.com> --------- Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
1 parent fd07901 commit ca0f3dc

30 files changed

Lines changed: 3338 additions & 32 deletions

File tree

codegen/go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ go 1.24.0
55
toolchain go1.25.0
66

77
require (
8-
golang.org/x/text v0.32.0
9-
golang.org/x/tools v0.40.0
8+
golang.org/x/text v0.34.0
9+
golang.org/x/tools v0.42.0
1010
)
1111

1212
require (
13-
golang.org/x/mod v0.31.0 // indirect
13+
golang.org/x/mod v0.33.0 // indirect
1414
golang.org/x/sync v0.19.0 // indirect
1515
)

codegen/go.sum

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
22
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
3-
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
4-
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
3+
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
4+
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
55
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
66
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
7-
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
8-
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
9-
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
10-
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
7+
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
8+
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
9+
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
10+
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=

docs/doc-site/project/maintainers/ROADMAP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ timeline
5858

5959
1. [x] Jan 2026: all go-openapi projects adopts the forked testify
6060
2. [ ] Feb 2026: all go-openapi projects transition to generics
61-
3. [ ] Mar 2026: go-swagger transitions to the forked testify
61+
3. [x] Mar 2026: go-swagger transitions to the forked testify
6262

6363
### What won't come anytime soon
6464

docs/doc-site/usage/MIGRATION.md

Lines changed: 179 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,127 @@ weight: 20
66

77
## Migration Guide from stretchr/testify v1
88

9-
### 1. Update Import Path
9+
This guide covers migrating from `stretchr/testify` to `go-openapi/testify/v2`.
10+
You can use the [automated migration tool](#automated-migration-tool) or migrate [manually](#manual-migration).
11+
12+
### Automated Migration Tool
13+
14+
`migrate-testify` automates both the import migration (pass 1) and the generic
15+
upgrade (pass 2). It uses `go/packages` and `go/types` for type-checked,
16+
semantics-preserving transformations.
17+
18+
#### Installation
19+
20+
```bash
21+
go install github.com/go-openapi/testify/hack/migrate-testify/v2@latest
22+
```
23+
24+
This installs the `migrate-testify` binary into your `$GOBIN`.
25+
26+
#### Quick Start
27+
28+
```bash
29+
# Run both passes on the current directory (preview first, then apply)
30+
migrate-testify --all --dry-run .
31+
migrate-testify --all .
32+
33+
# Or run each pass separately
34+
migrate-testify --migrate .
35+
migrate-testify --upgrade-generics .
36+
```
37+
38+
#### Pass 1: Import Migration (`--migrate`)
39+
40+
Rewrites `stretchr/testify` imports to `go-openapi/testify/v2`:
41+
42+
```bash
43+
# Dry-run to preview changes
44+
migrate-testify --migrate --dry-run .
45+
46+
# Apply changes
47+
migrate-testify --migrate .
48+
```
49+
50+
This pass handles:
51+
- Import path rewriting (`assert`, `require`, root package)
52+
- Function renames (`EventuallyWithT` to `EventuallyWith`, `NoDirExists` to `DirNotExists`, etc.)
53+
- Type replacement (`PanicTestFunc` to `func()`)
54+
- YAML enable import injection (adds `_ "github.com/go-openapi/testify/v2/enable/yaml"` when `YAMLEq` is used)
55+
- Incompatible import detection (`mock`, `suite`, `http` packages emit warnings with guidance)
56+
- `go.mod` update (drops `stretchr/testify`, adds `go-openapi/testify/v2`)
57+
58+
#### Pass 2: Generic Upgrade (`--upgrade-generics`)
59+
60+
Upgrades reflection-based assertions to generic variants where types are statically
61+
resolvable and the semantics are preserved:
62+
63+
```bash
64+
# Dry-run to preview changes
65+
migrate-testify --upgrade-generics --dry-run .
66+
67+
# Apply changes
68+
migrate-testify --upgrade-generics .
69+
```
70+
71+
The tool is conservative: it only upgrades when:
72+
- Argument types are statically known (no `any`, no `interface{}`)
73+
- Types satisfy the required constraint (`comparable`, `Ordered`, `Text`, etc.)
74+
- For `Equal`/`NotEqual`: types are "deeply comparable" (no pointers or structs with pointer fields)
75+
- For `Contains`: the container type disambiguates to `StringContainsT`, `SliceContainsT`, or `MapContainsT`
76+
- `IsType` is flagged for manual review (argument count changes)
77+
78+
Assertions that cannot be safely upgraded are tracked and reported in the summary with
79+
a specific reason (e.g., "pointer type", "interface{}/any", "type mismatch").
80+
Use `--verbose` to see the file and line of each skipped assertion.
81+
82+
#### Reference
83+
84+
```
85+
Usage: migrate-testify [flags] [directory]
86+
87+
Migrate stretchr/testify to go-openapi/testify/v2 and upgrade to generic assertions.
88+
89+
Flags:
90+
-all Run both passes sequentially
91+
-dry-run Show diffs without modifying files
92+
-migrate Run pass 1: stretchr/testify -> go-openapi/testify/v2
93+
-upgrade-generics Run pass 2: reflection -> generic assertions
94+
-verbose Print detailed transformation info
95+
-skip-gomod Skip go.mod changes
96+
-skip-vendor Skip vendor/ directory (default true)
97+
-version string Target testify version (default "v2.3.0")
98+
99+
At least one of --migrate, --upgrade-generics, or --all is required.
100+
101+
Mono-repo support:
102+
Pass 1 walks the filesystem and works across module boundaries.
103+
Pass 2 requires type information and uses go/packages to load code.
104+
For multi-module repos, a go.work file must be present so that pass 2
105+
can load all workspace modules. Create one with:
106+
go work init . ./sub/module1 ./sub/module2 ...
107+
108+
Post-migration checklist:
109+
- Run your linter: the migration may surface pre-existing unchecked linting issues.
110+
- Run your test suite to verify all tests still pass.
111+
```
112+
113+
---
114+
115+
### Manual Migration
116+
117+
#### 1. Update Import Paths
10118

11119
```go
12120
// Old
13-
```go
14-
import "github.com/stretchr/testify/v2"
121+
import "github.com/stretchr/testify/assert"
122+
import "github.com/stretchr/testify/require"
15123

16124
// New
17-
import "github.com/go-openapi/testify/v2"
125+
import "github.com/go-openapi/testify/v2/assert"
126+
import "github.com/go-openapi/testify/v2/require"
18127
```
19128

20-
### 2. Optional: Enable YAML Support
129+
#### 2. Optional: Enable YAML Support
21130

22131
If you use `YAMLEq` assertions: this feature is now opt-in.
23132

@@ -27,7 +136,7 @@ import _ "github.com/go-openapi/testify/enable/yaml/v2"
27136

28137
Without this import, YAML assertions will panic with a helpful error message.
29138

30-
### 3. Optional: Enable Colorized Output
139+
#### 3. Optional: Enable Colorized Output
31140

32141
```go
33142
import _ "github.com/go-openapi/testify/enable/colors/v2"
@@ -43,11 +152,11 @@ go test -v -testify.colorized -testify.theme=light .
43152

44153
![Colorized Test](colorized.png)
45154

46-
### 4. Optional: Adopt Generic Assertions
155+
#### 4. Optional: Adopt Generic Assertions
47156

48157
For better type safety and performance, consider migrating to generic assertion variants. This is entirely optional—reflection-based assertions continue to work as before.
49158

50-
#### Step 1: Identify Generic-Capable Assertions
159+
##### Identify Generic-Capable Assertions
51160

52161
Look for these common assertions in your tests:
53162

@@ -75,8 +184,6 @@ assert.IsDecreasing → assert.IsDecreasingT
75184
assert.IsType(t, User{}, v) → assert.IsOfTypeT[User](t, v) // No dummy value!
76185
```
77186

78-
#### Step 2: Add Type Suffix
79-
80187
Simply add `T` to the function name. The compiler will check types automatically:
81188

82189
```go
@@ -89,14 +196,14 @@ assert.EqualT(t, expected, actual)
89196
assert.ElementsMatchT(t, slice1, slice2)
90197
```
91198

92-
#### Step 3: Fix Type Mismatches
199+
##### Fix Type Mismatches
93200

94201
The compiler will now catch type errors. This is a feature—it reveals bugs:
95202

96203
```go
97204
// Compiler catches this
98205
assert.EqualT(t, int64(42), int32(42))
99-
// Error: mismatched types int64 and int32
206+
// Error: mismatched types int64 and int32
100207

101208
// Fix: Use same type
102209
assert.EqualT(t, int64(42), int64(actual))
@@ -105,7 +212,29 @@ assert.EqualT(t, int64(42), int64(actual))
105212
assert.Equal(t, int64(42), int32(42)) // Still works
106213
```
107214

108-
#### Benefits of Migration
215+
##### Pointer Semantics: When NOT to Upgrade
216+
217+
Generic assertions use Go's `==` operator, while reflection-based assertions use `reflect.DeepEqual`.
218+
For most types these are equivalent, but **they differ for pointers and structs containing pointers**:
219+
220+
```go
221+
a := &MyStruct{Name: "alice"}
222+
b := &MyStruct{Name: "alice"}
223+
224+
assert.Equal(t, a, b) // PASSES (reflect.DeepEqual compares pointed-to values)
225+
assert.EqualT(t, a, b) // FAILS (== compares pointer addresses)
226+
```
227+
228+
**Do not upgrade to generic variants when:**
229+
- Arguments are pointer types (`*T`) — `EqualT` compares addresses, not values
230+
- Arguments are structs with pointer fields — `==` compares field addresses, `DeepEqual` compares field values
231+
- You intentionally rely on cross-type comparison (`int64` vs `int32`)
232+
233+
The automated migration tool handles this automatically by only upgrading
234+
assertions where the argument types are "deeply comparable" — types where `==` and
235+
`reflect.DeepEqual` produce the same result.
236+
237+
##### Benefits of Generic Assertions
109238

110239
- **Compile-time type safety**: Catch errors when writing tests
111240
- **Performance**: 1.2x to 81x faster (see [Benchmarks](../project/maintainers/BENCHMARKS.md))
@@ -114,33 +243,62 @@ assert.Equal(t, int64(42), int32(42)) // Still works
114243

115244
See the [Generics Guide](./GENERICS.md) for detailed usage patterns and best practices.
116245

117-
### 5. Remove Suite/Mock Usage
246+
#### 5. Remove Suite/Mock Usage
118247

119-
Replace testify mocks with:
248+
Replace testify mocks with:
120249
- [mockery](https://github.com/vektra/mockery) for mocking
121250
Replace testify suites with:
122251
- Standard Go subtests for test organization
123252
- or wait until we reintroduce this feature (possible, but not certain)
124253

125-
### 6. Remove use of the `testify/http` package
254+
#### 6. Replace `go.uber.org/goleak` with `NoGoRoutineLeak`
255+
256+
If you use `go.uber.org/goleak` to detect goroutine leaks in tests, consider replacing it
257+
with `assert.NoGoRoutineLeak` (or `require.NoGoRoutineLeak`), which is built into testify v2.
258+
259+
```go
260+
// Before (with goleak)
261+
import "go.uber.org/goleak"
262+
263+
func TestNoLeak(t *testing.T) {
264+
defer goleak.VerifyNone(t)
265+
// ... test code ...
266+
}
267+
268+
// After (with testify v2)
269+
import "github.com/go-openapi/testify/v2/assert"
270+
271+
func TestNoLeak(t *testing.T) {
272+
assert.NoGoRoutineLeak(t, func() {
273+
// ... test code ...
274+
})
275+
}
276+
```
277+
278+
This removes the `go.uber.org/goleak` dependency. This step is not automated by the
279+
migration tool.
280+
281+
#### 7. Remove use of the `testify/http` package
126282

127283
If you were still using the deprecated package `github.com/stretchr/testitfy/http`,
128284
you'll need to replace it by the standard `net/http/httptest` package.
129285

130286
We won't reintroduce this package ever.
131287

288+
---
289+
132290
## Breaking Changes Summary
133291

134292
### Removed Packages
135293

136-
- `suite` - Use standard Go subtests
137-
- `mock` - Use [mockery](https://github.com/vektra/mockery)
138-
- `http` - May be reintroduced later
294+
- `suite` - Use standard Go subtests
295+
- `mock` - Use [mockery](https://github.com/vektra/mockery)
296+
- `http` - May be reintroduced later
139297

140298
### Removed Functions and Types
141299

142-
- All deprecated functions from v1 removed
143-
- Removed extraneous "helper" types: `PanicTestFunc` (`func()`)
300+
- All deprecated functions from v1 removed
301+
- Removed extraneous "helper" types: `PanicTestFunc` (`func()`)
144302

145303
### Behavior Changes
146304

@@ -156,4 +314,3 @@ Make sure to check the [behavior changes](./CHANGES.md) as we have fixed a few q
156314
- [Generics Guide](./GENERICS.md) - Learn about the 38 new type-safe generic assertions
157315
- [Usage Guide](./USAGE.md) - API conventions and how to navigate the documentation
158316
- [Tutorial](./TUTORIAL.md) - Best practices for writing tests with testify v2
159-

go.work

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use (
33
./codegen
44
./enable/colors
55
./enable/yaml
6+
./hack/migrate-testify
67
./internal/testintegration
78
)
89

go.work.sum

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
11
github.com/go-openapi/testify/v2 v2.0.1/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
22
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
33
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
4+
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
5+
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
46
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
57
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
8+
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
9+
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
610
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
711
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
12+
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
13+
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
814
golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc h1:bH6xUXay0AIFMElXG2rQ4uiE+7ncwtiOdPfYK1NK2XA=
915
golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ=
16+
golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2/go.mod h1:b7fPSJ0pKZ3ccUh8gnTONJxhn3c/PS6tyzQvyqw4iA8=
17+
golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4/go.mod h1:g5NllXBEermZrmR51cJDQxmJUHUOfRAaNyWBM+R+548=
18+
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
19+
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
20+
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
21+
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=

hack/migrate-testify/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
migrate-testify

0 commit comments

Comments
 (0)