Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions codegen/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ go 1.24.0
toolchain go1.25.0

require (
golang.org/x/text v0.32.0
golang.org/x/tools v0.40.0
golang.org/x/text v0.34.0
golang.org/x/tools v0.42.0
)

require (
golang.org/x/mod v0.31.0 // indirect
golang.org/x/mod v0.33.0 // indirect
golang.org/x/sync v0.19.0 // indirect
)
12 changes: 6 additions & 6 deletions codegen/go.sum
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
2 changes: 1 addition & 1 deletion docs/doc-site/project/maintainers/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ timeline

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

### What won't come anytime soon

Expand Down
201 changes: 179 additions & 22 deletions docs/doc-site/usage/MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,127 @@ weight: 20

## Migration Guide from stretchr/testify v1

### 1. Update Import Path
This guide covers migrating from `stretchr/testify` to `go-openapi/testify/v2`.
You can use the [automated migration tool](#automated-migration-tool) or migrate [manually](#manual-migration).

### Automated Migration Tool

`migrate-testify` automates both the import migration (pass 1) and the generic
upgrade (pass 2). It uses `go/packages` and `go/types` for type-checked,
semantics-preserving transformations.

#### Installation

```bash
go install github.com/go-openapi/testify/hack/migrate-testify/v2@latest
```

This installs the `migrate-testify` binary into your `$GOBIN`.

#### Quick Start

```bash
# Run both passes on the current directory (preview first, then apply)
migrate-testify --all --dry-run .
migrate-testify --all .

# Or run each pass separately
migrate-testify --migrate .
migrate-testify --upgrade-generics .
```

#### Pass 1: Import Migration (`--migrate`)

Rewrites `stretchr/testify` imports to `go-openapi/testify/v2`:

```bash
# Dry-run to preview changes
migrate-testify --migrate --dry-run .

# Apply changes
migrate-testify --migrate .
```

This pass handles:
- Import path rewriting (`assert`, `require`, root package)
- Function renames (`EventuallyWithT` to `EventuallyWith`, `NoDirExists` to `DirNotExists`, etc.)
- Type replacement (`PanicTestFunc` to `func()`)
- YAML enable import injection (adds `_ "github.com/go-openapi/testify/v2/enable/yaml"` when `YAMLEq` is used)
- Incompatible import detection (`mock`, `suite`, `http` packages emit warnings with guidance)
- `go.mod` update (drops `stretchr/testify`, adds `go-openapi/testify/v2`)

#### Pass 2: Generic Upgrade (`--upgrade-generics`)

Upgrades reflection-based assertions to generic variants where types are statically
resolvable and the semantics are preserved:

```bash
# Dry-run to preview changes
migrate-testify --upgrade-generics --dry-run .

# Apply changes
migrate-testify --upgrade-generics .
```

The tool is conservative: it only upgrades when:
- Argument types are statically known (no `any`, no `interface{}`)
- Types satisfy the required constraint (`comparable`, `Ordered`, `Text`, etc.)
- For `Equal`/`NotEqual`: types are "deeply comparable" (no pointers or structs with pointer fields)
- For `Contains`: the container type disambiguates to `StringContainsT`, `SliceContainsT`, or `MapContainsT`
- `IsType` is flagged for manual review (argument count changes)

Assertions that cannot be safely upgraded are tracked and reported in the summary with
a specific reason (e.g., "pointer type", "interface{}/any", "type mismatch").
Use `--verbose` to see the file and line of each skipped assertion.

#### Reference

```
Usage: migrate-testify [flags] [directory]

Migrate stretchr/testify to go-openapi/testify/v2 and upgrade to generic assertions.

Flags:
-all Run both passes sequentially
-dry-run Show diffs without modifying files
-migrate Run pass 1: stretchr/testify -> go-openapi/testify/v2
-upgrade-generics Run pass 2: reflection -> generic assertions
-verbose Print detailed transformation info
-skip-gomod Skip go.mod changes
-skip-vendor Skip vendor/ directory (default true)
-version string Target testify version (default "v2.3.0")

At least one of --migrate, --upgrade-generics, or --all is required.

Mono-repo support:
Pass 1 walks the filesystem and works across module boundaries.
Pass 2 requires type information and uses go/packages to load code.
For multi-module repos, a go.work file must be present so that pass 2
can load all workspace modules. Create one with:
go work init . ./sub/module1 ./sub/module2 ...

Post-migration checklist:
- Run your linter: the migration may surface pre-existing unchecked linting issues.
- Run your test suite to verify all tests still pass.
```

---

### Manual Migration

#### 1. Update Import Paths

```go
// Old
```go
import "github.com/stretchr/testify/v2"
import "github.com/stretchr/testify/assert"
import "github.com/stretchr/testify/require"

// New
import "github.com/go-openapi/testify/v2"
import "github.com/go-openapi/testify/v2/assert"
import "github.com/go-openapi/testify/v2/require"
```

### 2. Optional: Enable YAML Support
#### 2. Optional: Enable YAML Support

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

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

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

### 3. Optional: Enable Colorized Output
#### 3. Optional: Enable Colorized Output

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

![Colorized Test](colorized.png)

### 4. Optional: Adopt Generic Assertions
#### 4. Optional: Adopt Generic Assertions

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

#### Step 1: Identify Generic-Capable Assertions
##### Identify Generic-Capable Assertions

Look for these common assertions in your tests:

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

#### Step 2: Add Type Suffix

Simply add `T` to the function name. The compiler will check types automatically:

```go
Expand All @@ -89,14 +196,14 @@ assert.EqualT(t, expected, actual)
assert.ElementsMatchT(t, slice1, slice2)
```

#### Step 3: Fix Type Mismatches
##### Fix Type Mismatches

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

```go
// Compiler catches this
assert.EqualT(t, int64(42), int32(42))
// Error: mismatched types int64 and int32
// Error: mismatched types int64 and int32

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

#### Benefits of Migration
##### Pointer Semantics: When NOT to Upgrade

Generic assertions use Go's `==` operator, while reflection-based assertions use `reflect.DeepEqual`.
For most types these are equivalent, but **they differ for pointers and structs containing pointers**:

```go
a := &MyStruct{Name: "alice"}
b := &MyStruct{Name: "alice"}

assert.Equal(t, a, b) // PASSES (reflect.DeepEqual compares pointed-to values)
assert.EqualT(t, a, b) // FAILS (== compares pointer addresses)
```

**Do not upgrade to generic variants when:**
- Arguments are pointer types (`*T`) — `EqualT` compares addresses, not values
- Arguments are structs with pointer fields — `==` compares field addresses, `DeepEqual` compares field values
- You intentionally rely on cross-type comparison (`int64` vs `int32`)

The automated migration tool handles this automatically by only upgrading
assertions where the argument types are "deeply comparable" — types where `==` and
`reflect.DeepEqual` produce the same result.

##### Benefits of Generic Assertions

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

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

### 5. Remove Suite/Mock Usage
#### 5. Remove Suite/Mock Usage

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

### 6. Remove use of the `testify/http` package
#### 6. Replace `go.uber.org/goleak` with `NoGoRoutineLeak`

If you use `go.uber.org/goleak` to detect goroutine leaks in tests, consider replacing it
with `assert.NoGoRoutineLeak` (or `require.NoGoRoutineLeak`), which is built into testify v2.

```go
// Before (with goleak)
import "go.uber.org/goleak"

func TestNoLeak(t *testing.T) {
defer goleak.VerifyNone(t)
// ... test code ...
}

// After (with testify v2)
import "github.com/go-openapi/testify/v2/assert"

func TestNoLeak(t *testing.T) {
assert.NoGoRoutineLeak(t, func() {
// ... test code ...
})
}
```

This removes the `go.uber.org/goleak` dependency. This step is not automated by the
migration tool.

#### 7. Remove use of the `testify/http` package

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

We won't reintroduce this package ever.

---

## Breaking Changes Summary

### Removed Packages

- `suite` - Use standard Go subtests
- `mock` - Use [mockery](https://github.com/vektra/mockery)
- `http` - May be reintroduced later
- `suite` - Use standard Go subtests
- `mock` - Use [mockery](https://github.com/vektra/mockery)
- `http` - May be reintroduced later

### Removed Functions and Types

- All deprecated functions from v1 removed
- Removed extraneous "helper" types: `PanicTestFunc` (`func()`)
- All deprecated functions from v1 removed
- Removed extraneous "helper" types: `PanicTestFunc` (`func()`)

### Behavior Changes

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

1 change: 1 addition & 0 deletions go.work
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use (
./codegen
./enable/colors
./enable/yaml
./hack/migrate-testify
./internal/testintegration
)

Expand Down
12 changes: 12 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
github.com/go-openapi/testify/v2 v2.0.1/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc h1:bH6xUXay0AIFMElXG2rQ4uiE+7ncwtiOdPfYK1NK2XA=
golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ=
golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2/go.mod h1:b7fPSJ0pKZ3ccUh8gnTONJxhn3c/PS6tyzQvyqw4iA8=
golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4/go.mod h1:g5NllXBEermZrmR51cJDQxmJUHUOfRAaNyWBM+R+548=
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
1 change: 1 addition & 0 deletions hack/migrate-testify/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
migrate-testify
Loading
Loading