Skip to content
Open
52 changes: 51 additions & 1 deletion _codegen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ func parseTemplates() (*template.Template, *template.Template, error) {
funcTemplate = string(f)
}
tmpl, err := template.New("function").Funcs(template.FuncMap{
"replace": strings.ReplaceAll,
"replace": strings.ReplaceAll,
"requireCommentParseIf": requireCommentParseIf,
}).Parse(funcTemplate)
if err != nil {
return nil, nil, err
Expand Down Expand Up @@ -298,6 +299,55 @@ func (f *testFunc) CommentWithoutT(receiver string) string {
return strings.Replace(f.Comment(), search, replace, -1)
}

// requireCommentParseIf rewrites invalid "if require..." examples
// in generated documentation for the require package.
//
// The assert package documentation often shows conditional usage like:
//
// // if assert.NoError(t, err) {
// // // continue with test
// // }
//
// However, require package methods do not return bool values;
// they call t.FailNow() on failure. This function transforms
// such conditional blocks by removing the "if require.Function() {"
// wrapper and adjusting indentation to show proper usage:
//
// // require.NoError(t, err)
// // continue with test
func requireCommentParseIf(s string) string {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method should be tested

Copy link
Collaborator

@dolmen dolmen Aug 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is codegen. Tests are not strictly necessary: checking the generated output is enough.

However, tests would still be helpful to document what the function expects and does. Tests will also be useful for future refactorings.

Even more helpful would be some godoc for the function because the require prefix is misleading: on a first read I got it as verb while it is about the require package.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An issue is that it's unclear from the function name or the code; what is this function is supposed to do. A test or a good comment before the function would help.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my point, indeed

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#1780 (comment)
I wasn’t familiar with the reply feature, so I didn’t realize multiple conversations were happening. 😅
Considering tests, would it be better to separate this method and handle it individually?

Copy link
Collaborator

@dolmen dolmen Sep 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PCloud63514 Please start by adding a comment block to document the function.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a doc comment for requireCommentParseIf to clarify its purpose.requireCommentParseIf

lines := strings.Split(s, "\n")
out := make([]string, 0, len(lines))
rePrefix := regexp.MustCompile(`//[[:blank:]]+`)
ifBlock := false
prePrefix := "//\t"

for _, line := range lines {
commentPrefix := rePrefix.FindString(line)
comment := strings.TrimSpace(line[2:])

if ifBlock && strings.HasPrefix(comment, "}") {
ifBlock = false
continue
}

if strings.HasPrefix(comment, "if require.") && strings.HasSuffix(comment, "{") {
ifBlock = true
comment = strings.TrimPrefix(comment, "if ")
comment = strings.TrimSpace(comment)
comment = strings.TrimSuffix(comment, "{")
}

if ifBlock {
commentPrefix = prePrefix
}

prePrefix = commentPrefix
out = append(out, commentPrefix+comment)
}
return strings.Join(out, "\n")
}

// Standard header https://go.dev/s/generatedcode.
var headerTemplate = `// Code generated with github.com/stretchr/testify/_codegen; DO NOT EDIT.

Expand Down
43 changes: 43 additions & 0 deletions _codegen/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package main

import (
"testing"
)

func TestRequireCommentParseIf(t *testing.T) {
tests := []struct {
name string
input string
expected string
}{
{
name: "single line without if",
input: "// Simple comment line",
expected: "// Simple comment line",
},
{
name: "simple if require block transformation",
input: "//\tif require.NotEmpty(t, obj) {\n//\t require.Equal(t, \"two\", obj[1])\n//\t}",
expected: "//\trequire.NotEmpty(t, obj) \n//\trequire.Equal(t, \"two\", obj[1])",
},
{
name: "no if block - should remain unchanged",
input: "// Contains function\n//\trequire.Contains(t, \"Hello World\", \"World\")",
expected: "// Contains function\n//\trequire.Contains(t, \"Hello World\", \"World\")",
},
{
name: "mixed content with if block",
input: "//\t actualObj, err := SomeFunction()\n//\tif require.NoError(t, err) {\n//\t\t do something\n//\t}",
expected: "//\t actualObj, err := SomeFunction()\n//\t require.NoError(t, err) \n//\t do something",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := requireCommentParseIf(tt.input)
if result != tt.expected {
t.Errorf("requireCommentParseIf() failed:\nInput: %q\nGot: %q\nWant: %q", tt.input, result, tt.expected)
}
})
}
}
24 changes: 10 additions & 14 deletions require/require.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion require/require.go.tmpl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{{ replace .Comment "assert." "require."}}
{{ replace .Comment "assert." "require." | requireCommentParseIf }}
func {{.DocInfo.Name}}(t TestingT, {{.Params}}) {
if h, ok := t.(tHelper); ok { h.Helper() }
if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return }
Expand Down