diff --git a/docs/configuration.md b/docs/configuration.md index 67b69cc5..f2bfcac6 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -19,11 +19,11 @@ Formatting If a parameter is named `with-expecter` and we want a value of `True`, then these are the formats for each source: -| source | value | -|--------|-------| -| command line | `--with-expecter=true` | +| source | value | +|----------------------|------------------------------| +| command line | `--with-expecter=true` | | Environment variable | `MOCKERY_WITH_EXPECTER=True` | -| yaml | `with-expecter: True` | +| yaml | `with-expecter: True` | Recommended Basic Config ------------------------- @@ -57,27 +57,28 @@ Parameter Descriptions Please see the [migration docs](migrating_to_packages.md) for details on how to migrate your config. -| name | templated | default | description | -|------|-----------|---------|-------------| -| `all` | :fontawesome-solid-x: | `#!yaml false` | Generate all interfaces for the specified packages. | -| `boilerplate-file` | :fontawesome-solid-x: | `#!yaml ""` | Specify a path to a file that contains comments you want displayed at the top of all generated mock files. This is commonly used to display license headers at the top of your source code. | -| `config` | :fontawesome-solid-x: | `#!yaml ""` | Set the location of the mockery config file. | -| `dir` | :fontawesome-solid-check: | `#!yaml "mocks/{{.PackagePath}}"` | The directory where the mock file will be outputted to. | -| `disable-config-search` | :fontawesome-solid-x: | `#!yaml false` | Disable searching for configuration files | -| `disable-version-string` | :fontawesome-solid-x: | `#!yaml false` | Disable the version string in the generated mock files. | -| `dry-run` | :fontawesome-solid-x: | `#!yaml false` | Print the actions that would be taken, but don't perform the actions. | -| `filename` | :fontawesome-solid-check: | `#!yaml "mock_{{.InterfaceName}}.go"` | The name of the file the mock will reside in. | -| `include-auto-generated` | :fontawesome-solid-x: | `#!yaml true` | Set to `#!yaml false` if you need mockery to skip auto-generated files during its recursive package discovery. When set to `#!yaml true`, mockery includes auto-generated files when determining if a particular directory is an importable package. | -| `inpackage` | :fontawesome-solid-x: | `#!yaml false` | When generating mocks alongside the original interfaces, you must specify `inpackage: True` to inform mockery that the mock is being placed in the same package as the original interface. | -| `mockname` | :fontawesome-solid-check: | `#!yaml "Mock{{.InterfaceName}}"` | The name of the generated mock. | -| `outpkg` | :fontawesome-solid-check: | `#!yaml "{{.PackageName}}"` | Use `outpkg` to specify the package name of the generated mocks. | -| `log-level` | :fontawesome-solid-x: | `#!yaml "info"` | Set the level of the logger | -| [`packages`](features.md#packages-configuration) | :fontawesome-solid-x: | `#!yaml null` | A dictionary containing configuration describing the packages and interfaces to generate mocks for. | -| `print` | :fontawesome-solid-x: | `#!yaml false` | Use `print: True` to have the resulting code printed out instead of written to disk. | -| [`recursive`](features.md#recursive-package-discovery) | :fontawesome-solid-x: | `#!yaml false` | When set to `true` on a particular package, mockery will recursively search for all sub-packages and inject those packages into the config map. | -| `tags` | :fontawesome-solid-x: | `#!yaml ""` | Set the build tags of the generated mocks. | -| [`with-expecter`](features.md#expecter-structs) | :fontawesome-solid-x: | `#!yaml true` | Use `with-expecter: True` to generate `EXPECT()` methods for your mocks. This is the preferred way to setup your mocks. | -| [`replace-type`](features.md#replace-types) | :fontawesome-solid-x: | `#!yaml null` | Replaces aliases, packages and/or types during generation.| +| name | templated | default | description | +|--------------------------------------------------------|---------------------------|---------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `all` | :fontawesome-solid-x: | `#!yaml false` | Generate all interfaces for the specified packages. | +| `boilerplate-file` | :fontawesome-solid-x: | `#!yaml ""` | Specify a path to a file that contains comments you want displayed at the top of all generated mock files. This is commonly used to display license headers at the top of your source code. | +| `config` | :fontawesome-solid-x: | `#!yaml ""` | Set the location of the mockery config file. | +| `dir` | :fontawesome-solid-check: | `#!yaml "mocks/{{.PackagePath}}"` | The directory where the mock file will be outputted to. | +| `disable-config-search` | :fontawesome-solid-x: | `#!yaml false` | Disable searching for configuration files | +| `disable-version-string` | :fontawesome-solid-x: | `#!yaml false` | Disable the version string in the generated mock files. | +| `dry-run` | :fontawesome-solid-x: | `#!yaml false` | Print the actions that would be taken, but don't perform the actions. | +| `filename` | :fontawesome-solid-check: | `#!yaml "mock_{{.InterfaceName}}.go"` | The name of the file the mock will reside in. | +| `include-auto-generated` | :fontawesome-solid-x: | `#!yaml true` | Set to `#!yaml false` if you need mockery to skip auto-generated files during its recursive package discovery. When set to `#!yaml true`, mockery includes auto-generated files when determining if a particular directory is an importable package. | +| `include-regex` | :fontawesome-solid-x: | `#!yaml ""` | When set, only interface names that match the expression will be generated. This setting is ignored if `all: True` is specified in the configuration | +| `inpackage` | :fontawesome-solid-x: | `#!yaml false` | When generating mocks alongside the original interfaces, you must specify `inpackage: True` to inform mockery that the mock is being placed in the same package as the original interface. | +| `mockname` | :fontawesome-solid-check: | `#!yaml "Mock{{.InterfaceName}}"` | The name of the generated mock. | +| `outpkg` | :fontawesome-solid-check: | `#!yaml "{{.PackageName}}"` | Use `outpkg` to specify the package name of the generated mocks. | +| `log-level` | :fontawesome-solid-x: | `#!yaml "info"` | Set the level of the logger | +| [`packages`](features.md#packages-configuration) | :fontawesome-solid-x: | `#!yaml null` | A dictionary containing configuration describing the packages and interfaces to generate mocks for. | +| `print` | :fontawesome-solid-x: | `#!yaml false` | Use `print: True` to have the resulting code printed out instead of written to disk. | +| [`recursive`](features.md#recursive-package-discovery) | :fontawesome-solid-x: | `#!yaml false` | When set to `true` on a particular package, mockery will recursively search for all sub-packages and inject those packages into the config map. | +| `tags` | :fontawesome-solid-x: | `#!yaml ""` | Set the build tags of the generated mocks. | +| [`with-expecter`](features.md#expecter-structs) | :fontawesome-solid-x: | `#!yaml true` | Use `with-expecter: True` to generate `EXPECT()` methods for your mocks. This is the preferred way to setup your mocks. | +| [`replace-type`](features.md#replace-types) | :fontawesome-solid-x: | `#!yaml null` | Replaces aliases, packages and/or types during generation. | Layouts ------- @@ -186,19 +187,19 @@ Template Variables Variables that are marked as being templated are capable of using mockery-provided template parameters. -| name | description | -|------|-------------| -| InterfaceDir | The directory path of the original interface being mocked. This can be used as
`#!yaml dir: "{{.InterfaceDir}}"` to place your mocks adjacent to the original interface. This should not be used for external interfaces. | -| InterfaceDirRelative | The directory path of the original interface being mocked, relative to the current working directory. If the path cannot be made relative to the current working directory, this variable will be set equal to `PackagePath` | -| InterfaceName | The name of the original interface being mocked | -| InterfaceNameCamel | Converts a string `interface_name` to `InterfaceName` | -| InterfaceNameLowerCamel | Converts `InterfaceName` to `interfaceName` | -| InterfaceNameSnake | Converts `InterfaceName` to `interface_name` | -| InterfaceNameLower | Converts `InterfaceName` to `interfacename` | -| Mock | A string that is `Mock` if the interface is exported, or `mock` if it is not exported. Useful when setting the name of your mock to something like:
`#!yaml mockname: "{{.Mock}}{{.InterfaceName}}"`
This way, the mock name will retain the exported-ness of the original interface. -| MockName | The name of the mock that will be generated. Note that this is simply the `mockname` configuration variable | -| PackageName | The name of the package from the original interface | -| PackagePath | The fully qualified package path of the original interface | +| name | description | +|-------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| InterfaceDir | The directory path of the original interface being mocked. This can be used as
`#!yaml dir: "{{.InterfaceDir}}"` to place your mocks adjacent to the original interface. This should not be used for external interfaces. | +| InterfaceDirRelative | The directory path of the original interface being mocked, relative to the current working directory. If the path cannot be made relative to the current working directory, this variable will be set equal to `PackagePath` | +| InterfaceName | The name of the original interface being mocked | +| InterfaceNameCamel | Converts a string `interface_name` to `InterfaceName` | +| InterfaceNameLowerCamel | Converts `InterfaceName` to `interfaceName` | +| InterfaceNameSnake | Converts `InterfaceName` to `interface_name` | +| InterfaceNameLower | Converts `InterfaceName` to `interfacename` | +| Mock | A string that is `Mock` if the interface is exported, or `mock` if it is not exported. Useful when setting the name of your mock to something like:
`#!yaml mockname: "{{.Mock}}{{.InterfaceName}}"`
This way, the mock name will retain the exported-ness of the original interface. | +| MockName | The name of the mock that will be generated. Note that this is simply the `mockname` configuration variable | +| PackageName | The name of the package from the original interface | +| PackagePath | The fully qualified package path of the original interface | Template functions ------------------ diff --git a/docs/features.md b/docs/features.md index 2805198f..748fc44c 100644 --- a/docs/features.md +++ b/docs/features.md @@ -181,6 +181,20 @@ You can use the `showconfig` command to see the config mockery injects. The outp ??? note "performance characteristics" The performance when using `#!yaml recursive: true` may be worse than manually specifying all packages statically in the yaml file. This is because of the fact that mockery has to recursively walk the filesystem path that contains the package in question. It may unnecessarily walk down unrelated paths (for example, a Python virtual environment that is in the same path as your package). For this reason, it is recommended _not_ to use `#!yaml recursive: true` if it can be avoided. +### Regex matching + +You can filter matched interfaces using the `include-regex` option. To generate mocks only for interfaces ending in `Client` we can use the following configuration: + +```yaml +packages: + github.com/user/project: + config: + recursive: true + include-regex: ".*Client" +``` + +??? note "all: true" + Using `all: true` will override `include-regex` and issue a warning. Mock Constructors ----------------- diff --git a/pkg/config/config.go b/pkg/config/config.go index 70a114f0..3fd013ad 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -7,6 +7,7 @@ import ( "fmt" "os" "reflect" + "regexp" "strings" "github.com/chigopher/pathlib" @@ -43,6 +44,7 @@ type Config struct { Exported bool `mapstructure:"exported"` FileName string `mapstructure:"filename"` IncludeAutoGenerated bool `mapstructure:"include-auto-generated"` + IncludeRegex string `mapstructure:"include-regex"` InPackage bool `mapstructure:"inpackage"` InPackageSuffix bool `mapstructure:"inpackage-suffix"` KeepTree bool `mapstructure:"keeptree"` @@ -83,13 +85,13 @@ func NewConfigFromViper(v *viper.Viper) (*Config, error) { Config: v.ConfigFileUsed(), } - packages, err := c.GetPackages(context.Background()) + packageList, err := c.GetPackages(context.Background()) if err != nil { return c, fmt.Errorf("failed to get packages: %w", err) } // Set defaults - if len(packages) == 0 { + if len(packageList) == 0 { v.SetDefault("case", "camel") v.SetDefault("dir", ".") v.SetDefault("output", "./mocks") @@ -185,11 +187,11 @@ func (c *Config) GetPackages(ctx context.Context) ([]string, error) { log.Error().Msg(msg) return []string{}, fmt.Errorf(msg) } - packages := []string{} + packageList := []string{} for key := range packageSection { - packages = append(packages, key) + packageList = append(packageList, key) } - return packages, nil + return packageList, nil } func (c *Config) getPackageConfigMap(ctx context.Context, packageName string) (map[string]any, error) { @@ -268,7 +270,20 @@ func (c *Config) ShouldGenerateInterface(ctx context.Context, packageName, inter return false, err } _, interfaceExists := interfacesSection[interfaceName] - return pkgConfig.All || interfaceExists, nil + + var matchedByRegex bool + if pkgConfig.IncludeRegex != "" { + if pkgConfig.All { + log := zerolog.Ctx(ctx) + log.Warn().Msg("interface config has both `all` and `include-regex` set. `include-regex` will be ignored") + } else { + matchedByRegex, err = regexp.MatchString(pkgConfig.IncludeRegex, interfaceName) + if err != nil { + return false, err + } + } + } + return pkgConfig.All || interfaceExists || matchedByRegex, nil } func (c *Config) getInterfacesSection(ctx context.Context, packageName string) (map[string]any, error) { @@ -552,11 +567,11 @@ func (c *Config) subPackages( func (c *Config) discoverRecursivePackages(ctx context.Context) error { log := zerolog.Ctx(ctx) recursivePackages := map[string]*Config{} - packages, err := c.GetPackages(ctx) + packageList, err := c.GetPackages(ctx) if err != nil { return fmt.Errorf("failed to get packages: %w", err) } - for _, pkg := range packages { + for _, pkg := range packageList { pkgConfig, err := c.GetPackageConfig(ctx, pkg) if err != nil { return fmt.Errorf("failed to get package config: %w", err) diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 7772e56a..ffe4a7b0 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -608,6 +608,46 @@ func TestConfig_ShouldGenerateInterface(t *testing.T) { }, want: true, }, + { + name: "should generate using included-regex", + c: &Config{ + Packages: map[string]interface{}{ + "some_package": map[string]interface{}{ + "config": map[string]interface{}{ + "include-regex": ".*Interface", + }, + }, + }, + }, + want: true, + }, + { + name: "should generate when using all and included-regex doesn't match", + c: &Config{ + Packages: map[string]interface{}{ + "some_package": map[string]interface{}{ + "config": map[string]interface{}{ + "all": true, + "include-regex": ".*XInterface", + }, + }, + }, + }, + want: true, + }, + { + name: "should not generate when included-regex doesn't match", + c: &Config{ + Packages: map[string]interface{}{ + "some_package": map[string]interface{}{ + "config": map[string]interface{}{ + "include-regex": ".*XInterface", + }, + }, + }, + }, + want: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {