Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --constructor-prefix flag #28

Merged
merged 3 commits into from
Apr 29, 2022
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
23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,19 @@ Depending on how you prefer to structure your code, you can either

The following flags are defined by the binary.

| Name | Short Flag | Description |
| ------------------ | ---------- | ----------------------------------------------------------------------------------------------------------------- |
| Name | Short Flag | Description |
| ------------------ | ---------- | ----------- |
| package | p | The name of the generated package. Is the name of target directory if dirname or filename is supplied by default. |
| prefix | | A prefix used in the name of each mock struct. Should be TitleCase by convention. |
| interfaces | i | A list of interfaces to generate given the import paths. |
| exclude | e | A list of interfaces to exclude from generation. |
| filename | o | The target output file. All mocks are written to this file. |
| dirname | d | The target output directory. Each mock will be written to a unique file. |
| force | f | Do not abort if a write to disk would overwrite an existing file. |
| disable-formatting | | Do not run goimports over the rendered files (enabled by default). |
| goimports | | Path to the goimports binary (uses goimports on your PATH by default). |
| for-test | | Append _test suffix to generated package names and file names. |
| prefix | | A prefix used in the name of each mock struct. Should be TitleCase by convention. |
| constructor-prefix | | A prefix used in the name of each mock constructor function (after the initial `New`/`NewStrict` prefixes). Should be TitleCase by convention. |
| interfaces | i | A list of interfaces to generate given the import paths. |
| exclude | e | A list of interfaces to exclude from generation. |
| filename | o | The target output file. All mocks are written to this file. |
| dirname | d | The target output directory. Each mock will be written to a unique file. |
| force | f | Do not abort if a write to disk would overwrite an existing file. |
| disable-formatting | | Do not run goimports over the rendered files (enabled by default). |
| goimports | | Path to the goimports binary (uses goimports on your PATH by default). |
| for-test | | Append _test suffix to generated package names and file names. |

## Testing with Mocks

Expand Down
5 changes: 5 additions & 0 deletions cmd/go-mockgen/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func parseArgs() (*generation.Options, error) {
app.Flag("filename", "The target output file. All mocks are written to this file.").Short('o').StringVar(&opts.OutputFilename)
app.Flag("import-path", "The import path of the generated package. It will be inferred from the target directory by default.").StringVar(&opts.PkgName)
app.Flag("prefix", "A prefix used in the name of each mock struct. Should be TitleCase by convention.").StringVar(&opts.Prefix)
app.Flag("constructor-prefix", "A prefix used in the name of each mock constructor function (after the initial `New`/`NewStrict` prefixes). Should be TitleCase by convention.").StringVar(&opts.ConstructorPrefix)
app.Flag("force", "Do not abort if a write to disk would overwrite an existing file.").Short('f').BoolVar(&opts.Force)
app.Flag("disable-formatting", "Do not run goimports over the rendered files.").BoolVar(&opts.DisableFormatting)
app.Flag("goimports", "Path to the goimports binary.").Default("goimports").StringVar(&opts.GoImportsBinary)
Expand Down Expand Up @@ -120,6 +121,10 @@ func validateOptions(opts *generation.Options) (bool, error) {
return false, fmt.Errorf("prefix `%s` is illegal", opts.Prefix)
}

if opts.ConstructorPrefix != "" && !goIdentifierPattern.Match([]byte(opts.ConstructorPrefix)) {
return false, fmt.Errorf("constructor-`prefix `%s` is illegal", opts.ConstructorPrefix)
}

return false, nil
}

Expand Down
21 changes: 14 additions & 7 deletions internal/mockgen/generation/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Options struct {
OutputDir string
OutputImportPath string
Prefix string
ConstructorPrefix string
Force bool
DisableFormatting bool
GoImportsBinary string
Expand Down Expand Up @@ -105,7 +106,7 @@ func generateAndRender(ifaces []*types.Interface, filename string, opts *Options
pkgName += "_test"
}

content, err := generateContent(ifaces, pkgName, opts.Prefix, opts.OutputImportPath)
content, err := generateContent(ifaces, pkgName, opts.Prefix, opts.ConstructorPrefix, opts.OutputImportPath)
if err != nil {
return err
}
Expand All @@ -131,13 +132,13 @@ func generateAndRender(ifaces []*types.Interface, filename string, opts *Options
return nil
}

func generateContent(ifaces []*types.Interface, pkgName, prefix, outputImportPath string) (string, error) {
func generateContent(ifaces []*types.Interface, pkgName, prefix, constructorPrefix, outputImportPath string) (string, error) {
file := jen.NewFile(pkgName)
file.HeaderComment(fmt.Sprintf("Code generated by %s %s; DO NOT EDIT.", consts.Name, consts.Version))

for _, iface := range ifaces {
log.Printf("generating code for interface '%s'\n", iface.Name)
generateInterface(file, iface, prefix, outputImportPath)
generateInterface(file, iface, prefix, constructorPrefix, outputImportPath)
}

buffer := &bytes.Buffer{}
Expand All @@ -148,12 +149,18 @@ func generateContent(ifaces []*types.Interface, pkgName, prefix, outputImportPat
return buffer.String(), nil
}

func generateInterface(file *jen.File, iface *types.Interface, prefix, outputImportPath string) {
func generateInterface(file *jen.File, iface *types.Interface, prefix, constructorPrefix, outputImportPath string) {
withConstructorPrefix := func(f func(*wrappedInterface, string, string) jen.Code) func(*wrappedInterface, string) jen.Code {
return func(iface *wrappedInterface, outputImportPath string) jen.Code {
return f(iface, constructorPrefix, outputImportPath)
}
}

topLevelGenerators := []func(*wrappedInterface, string) jen.Code{
generateMockStruct,
generateMockStructConstructor,
generateMockStructStrictConstructor,
generateMockStructFromConstructor,
withConstructorPrefix(generateMockStructConstructor),
withConstructorPrefix(generateMockStructStrictConstructor),
withConstructorPrefix(generateMockStructFromConstructor),
}

methodGenerators := []func(*wrappedInterface, *wrappedMethod, string) jen.Code{
Expand Down
18 changes: 9 additions & 9 deletions internal/mockgen/generation/generate_constructors.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,53 +9,53 @@ import (
"github.com/derision-test/go-mockgen/internal/mockgen/types"
)

func generateMockStructConstructor(iface *wrappedInterface, outputImportPath string) jen.Code {
func generateMockStructConstructor(iface *wrappedInterface, constructorPrefix, outputImportPath string) jen.Code {
makeField := func(method *wrappedMethod) jen.Code {
return makeDefaultHookField(iface, method, generateNoopFunction(iface, method, outputImportPath))
}

name := fmt.Sprintf("New%s", iface.mockStructName)
name := fmt.Sprintf("New%s%s", constructorPrefix, iface.mockStructName)
commentText := []string{
fmt.Sprintf(`%s creates a new mock of the %s interface.`, name, iface.Name),
`All methods return zero values for all results, unless overwritten.`,
}
return generateConstructor(iface, strings.Join(commentText, " "), name, nil, makeField)
}

func generateMockStructStrictConstructor(iface *wrappedInterface, outputImportPath string) jen.Code {
func generateMockStructStrictConstructor(iface *wrappedInterface, constructorPrefix, outputImportPath string) jen.Code {
makeField := func(method *wrappedMethod) jen.Code {
return makeDefaultHookField(iface, method, generatePanickingFunction(iface, method, outputImportPath))
}

name := fmt.Sprintf("NewStrict%s", iface.mockStructName)
name := fmt.Sprintf("NewStrict%s%s", constructorPrefix, iface.mockStructName)
commentText := []string{
fmt.Sprintf(`%s creates a new mock of the %s interface.`, name, iface.Name),
`All methods panic on invocation, unless overwritten.`,
}
return generateConstructor(iface, strings.Join(commentText, " "), name, nil, makeField)
}

func generateMockStructFromConstructor(iface *wrappedInterface, outputImportPath string) jen.Code {
func generateMockStructFromConstructor(iface *wrappedInterface, constructorPrefix, outputImportPath string) jen.Code {
if !unicode.IsUpper([]rune(iface.Name)[0]) {
surrogateStructName := fmt.Sprintf("surrogateMock%s", iface.titleName)
surrogateDefinition := generateSurrogateInterface(iface, surrogateStructName)
name := jen.Id(surrogateStructName)
constructor := generateMockStructFromConstructorCommon(iface, name)
constructor := generateMockStructFromConstructorCommon(iface, name, constructorPrefix)
return compose(surrogateDefinition, constructor)
}

importPath := sanitizeImportPath(iface.ImportPath, outputImportPath)
name := jen.Qual(importPath, iface.Name)
return generateMockStructFromConstructorCommon(iface, name)
return generateMockStructFromConstructorCommon(iface, name, constructorPrefix)
}

func generateMockStructFromConstructorCommon(iface *wrappedInterface, ifaceName *jen.Statement) jen.Code {
func generateMockStructFromConstructorCommon(iface *wrappedInterface, ifaceName *jen.Statement, constructorPrefix string) jen.Code {
makeField := func(method *wrappedMethod) jen.Code {
// i.<MethodName>
return makeDefaultHookField(iface, method, jen.Id("i").Dot(method.Name))
}

name := fmt.Sprintf("New%sFrom", iface.mockStructName)
name := fmt.Sprintf("New%s%sFrom", constructorPrefix, iface.mockStructName)
commentText := []string{
fmt.Sprintf(`%s creates a new mock of the %s interface.`, name, iface.mockStructName),
`All methods delegate to the given implementation, unless overwritten.`,
Expand Down
8 changes: 4 additions & 4 deletions internal/mockgen/generation/generate_constructors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

func TestGenerateMockStructConstructor(t *testing.T) {
code := generateMockStructConstructor(makeInterface(TestMethodStatus, TestMethodDo, TestMethodDof))
code := generateMockStructConstructor(makeInterface(TestMethodStatus, TestMethodDo, TestMethodDof), "", "")
expected := strip(`
// NewMockTestClient creates a new mock of the Client interface. All methods
// return zero values for all results, unless overwritten.
Expand Down Expand Up @@ -36,7 +36,7 @@ func TestGenerateMockStructConstructor(t *testing.T) {
}

func TestGenerateMockStructStrictConstructor(t *testing.T) {
code := generateMockStructStrictConstructor(makeInterface(TestMethodStatus, TestMethodDo, TestMethodDof))
code := generateMockStructStrictConstructor(makeInterface(TestMethodStatus, TestMethodDo, TestMethodDof), "", "")
expected := strip(`
// NewStrictMockTestClient creates a new mock of the Client interface. All
// methods panic on invocation, unless overwritten.
Expand Down Expand Up @@ -64,7 +64,7 @@ func TestGenerateMockStructStrictConstructor(t *testing.T) {
}

func TestGenerateMockStructFromConstructor(t *testing.T) {
code := generateMockStructFromConstructor(makeInterface(TestMethodStatus, TestMethodDo, TestMethodDof))
code := generateMockStructFromConstructor(makeInterface(TestMethodStatus, TestMethodDo, TestMethodDof), "", "")
expected := strip(`
// NewMockTestClientFrom creates a new mock of the MockTestClient interface.
// All methods delegate to the given implementation, unless overwritten.
Expand All @@ -88,7 +88,7 @@ func TestGenerateMockStructFromConstructor(t *testing.T) {
func TestGenerateMockStructFromConstructorUnexported(t *testing.T) {
iface := makeBareInterface(TestMethodStatus, TestMethodDo, TestMethodDof)
iface.Name = "client"
code := generateMockStructFromConstructor(wrapInterface(iface, TestPrefix, TestTitleName, TestMockStructName, ""), "")
code := generateMockStructFromConstructor(wrapInterface(iface, TestPrefix, TestTitleName, TestMockStructName, ""), "", "")

expected := strip(`
// surrogateMockClient is a copy of the client interface (from the package
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import (
)

func TestGenerateMockFuncCallArgsMethod(t *testing.T) {
code := generateMockFuncCallArgsMethod(makeMethod(TestMethodDo))
wrappedInterface := makeInterface(TestMethodDo)
code := generateMockFuncCallArgsMethod(wrappedInterface, wrappedInterface.wrappedMethods[0], "")
expected := strip(`
// Args returns an interface slice containing the arguments of this
// invocation.
Expand All @@ -20,7 +21,8 @@ func TestGenerateMockFuncCallArgsMethod(t *testing.T) {
}

func TestGenerateMockFuncCallArgsMethodVariadic(t *testing.T) {
code := generateMockFuncCallArgsMethod(makeMethod(TestMethodDof))
wrappedInterface := makeInterface(TestMethodDof)
code := generateMockFuncCallArgsMethod(wrappedInterface, wrappedInterface.wrappedMethods[0], "")
expected := strip(`
// Args returns an interface slice containing the arguments of this
// invocation. The variadic slice argument is flattened in this array such
Expand All @@ -39,7 +41,8 @@ func TestGenerateMockFuncCallArgsMethodVariadic(t *testing.T) {
}

func TestGenerateMockFuncCallResultsMethod(t *testing.T) {
code := generateMockFuncCallResultsMethod(makeMethod(TestMethodDo))
wrappedInterface := makeInterface(TestMethodDo)
code := generateMockFuncCallResultsMethod(wrappedInterface, wrappedInterface.wrappedMethods[0], "")
expected := strip(`
// Results returns an interface slice containing the results of this
// invocation.
Expand All @@ -51,7 +54,8 @@ func TestGenerateMockFuncCallResultsMethod(t *testing.T) {
}

func TestGenerateMockFuncCallResultsMethodMultiple(t *testing.T) {
code := generateMockFuncCallResultsMethod(makeMethod(TestMethodStatus))
wrappedInterface := makeInterface(TestMethodStatus)
code := generateMockFuncCallResultsMethod(wrappedInterface, wrappedInterface.wrappedMethods[0], "")
expected := strip(`
// Results returns an interface slice containing the results of this
// invocation.
Expand Down
27 changes: 18 additions & 9 deletions internal/mockgen/generation/generate_mock_func_methods_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import (
)

func TestGenerateMockFuncSetHookMethod(t *testing.T) {
code := generateMockFuncSetHookMethod(makeMethod(TestMethodDo))
wrappedInterface := makeInterface(TestMethodDo)
code := generateMockFuncSetHookMethod(wrappedInterface, wrappedInterface.wrappedMethods[0], "")
expected := strip(`
// SetDefaultHook sets function that is called when the Do method of the
// parent MockTestClient instance is invoked and the hook queue is empty.
Expand All @@ -20,7 +21,8 @@ func TestGenerateMockFuncSetHookMethod(t *testing.T) {
}

func TestGenerateMockFuncSetHookMethodVariadic(t *testing.T) {
code := generateMockFuncSetHookMethod(makeMethod(TestMethodDof))
wrappedInterface := makeInterface(TestMethodDof)
code := generateMockFuncSetHookMethod(wrappedInterface, wrappedInterface.wrappedMethods[0], "")
expected := strip(`
// SetDefaultHook sets function that is called when the Dof method of the
// parent MockTestClient instance is invoked and the hook queue is empty.
Expand All @@ -32,7 +34,8 @@ func TestGenerateMockFuncSetHookMethodVariadic(t *testing.T) {
}

func TestGenerateMockFuncPushHookMethod(t *testing.T) {
code := generateMockFuncPushHookMethod(makeMethod(TestMethodDo))
wrappedInterface := makeInterface(TestMethodDo)
code := generateMockFuncPushHookMethod(wrappedInterface, wrappedInterface.wrappedMethods[0], "")
expected := strip(`
// PushHook adds a function to the end of hook queue. Each invocation of the
// Do method of the parent MockTestClient instance invokes the hook at the
Expand All @@ -48,7 +51,8 @@ func TestGenerateMockFuncPushHookMethod(t *testing.T) {
}

func TestGenerateMockFuncPushHookMethodVariadic(t *testing.T) {
code := generateMockFuncPushHookMethod(makeMethod(TestMethodDof))
wrappedInterface := makeInterface(TestMethodDof)
code := generateMockFuncPushHookMethod(wrappedInterface, wrappedInterface.wrappedMethods[0], "")
expected := strip(`
// PushHook adds a function to the end of hook queue. Each invocation of the
// Dof method of the parent MockTestClient instance invokes the hook at the
Expand All @@ -64,7 +68,8 @@ func TestGenerateMockFuncPushHookMethodVariadic(t *testing.T) {
}

func TestGenerateMockFuncSetReturnMethod(t *testing.T) {
code := generateMockFuncSetReturnMethod(makeMethod(TestMethodDo))
wrappedInterface := makeInterface(TestMethodDo)
code := generateMockFuncSetReturnMethod(wrappedInterface, wrappedInterface.wrappedMethods[0], "")
expected := strip(`
// SetDefaultReturn calls SetDefaultHook with a function that returns the
// given values.
Expand All @@ -78,7 +83,8 @@ func TestGenerateMockFuncSetReturnMethod(t *testing.T) {
}

func TestGenerateMockFuncPushReturnMethod(t *testing.T) {
code := generateMockFuncPushReturnMethod(makeMethod(TestMethodDo))
wrappedInterface := makeInterface(TestMethodDo)
code := generateMockFuncPushReturnMethod(wrappedInterface, wrappedInterface.wrappedMethods[0], "")
expected := strip(`
// PushReturn calls PushHook with a function that returns the given values.
func (f *TestClientDoFunc) PushReturn(r0 bool) {
Expand All @@ -91,7 +97,8 @@ func TestGenerateMockFuncPushReturnMethod(t *testing.T) {
}

func TestGenerateMockFuncNextHookMethod(t *testing.T) {
code := generateMockFuncNextHookMethod(makeMethod(TestMethodDo))
wrappedInterface := makeInterface(TestMethodDo)
code := generateMockFuncNextHookMethod(wrappedInterface, wrappedInterface.wrappedMethods[0], "")
expected := strip(`
func (f *TestClientDoFunc) nextHook() func(string) bool {
f.mutex.Lock()
Expand All @@ -110,7 +117,8 @@ func TestGenerateMockFuncNextHookMethod(t *testing.T) {
}

func TestGenerateMockFuncAppendCallMethod(t *testing.T) {
code := generateMockFuncAppendCallMethod(makeMethod(TestMethodDo))
wrappedInterface := makeInterface(TestMethodDo)
code := generateMockFuncAppendCallMethod(wrappedInterface, wrappedInterface.wrappedMethods[0], "")
expected := strip(`
func (f *TestClientDoFunc) appendCall(r0 TestClientDoFuncCall) {
f.mutex.Lock()
Expand All @@ -122,7 +130,8 @@ func TestGenerateMockFuncAppendCallMethod(t *testing.T) {
}

func TestGenerateMockFuncHistoryMethod(t *testing.T) {
code := generateMockFuncHistoryMethod(makeMethod(TestMethodDo))
wrappedInterface := makeInterface(TestMethodDo)
code := generateMockFuncHistoryMethod(wrappedInterface, wrappedInterface.wrappedMethods[0], "")
expected := strip(`
// History returns a sequence of TestClientDoFuncCall objects describing the
// invocations of this function.
Expand Down
6 changes: 4 additions & 2 deletions internal/mockgen/generation/generate_mock_methods_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import (
)

func TestGenerateMockInterfaceMethod(t *testing.T) {
code := generateMockInterfaceMethod(makeMethod(TestMethodDo))
wrappedInterface := makeInterface(TestMethodDo)
code := generateMockInterfaceMethod(wrappedInterface, wrappedInterface.wrappedMethods[0], "")
expected := strip(`
// Do delegates to the next hook function in the queue and stores the
// parameter and result values of this invocation.
Expand All @@ -22,7 +23,8 @@ func TestGenerateMockInterfaceMethod(t *testing.T) {
}

func TestGenerateMockInterfaceMethodVariadic(t *testing.T) {
code := generateMockInterfaceMethod(makeMethod(TestMethodDof))
wrappedInterface := makeInterface(TestMethodDof)
code := generateMockInterfaceMethod(wrappedInterface, wrappedInterface.wrappedMethods[0], "")
expected := strip(`
// Dof delegates to the next hook function in the queue and stores the
// parameter and result values of this invocation.
Expand Down
Loading