Skip to content

Commit

Permalink
feat: add support for nested interfaces
Browse files Browse the repository at this point in the history
Added support for interfaces that are defined inline inside a struct field.
  • Loading branch information
adamconnelly committed Jun 8, 2024
1 parent 877904a commit 020011e
Show file tree
Hide file tree
Showing 8 changed files with 930 additions and 70 deletions.
60 changes: 58 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ At the moment Kelpie is very much in development, and there are missing features

The following is a list of known-outstanding features:

- [ ] Generating mocks for inline interfaces in structs.
- [ ] Add the ability to customize the name, package and output folder of the generated mocks.
- [ ] Switch from `go:generate` to using a config file for more efficient mock generation in large code-bases.

## Quickstart

Expand Down Expand Up @@ -291,6 +292,61 @@ Similar to the way that you can match against no parameters with `kelpie.None[T]
mock.Setup(printer.Printf("Don't panic!", kelpie.AnyArgs[any]()).Panic("Ok!"))
```

### Nested Interfaces

Kelpie supports mocking interfaces defined as struct fields. This can be useful in situations where you want to define an interface to decouple and make testing easier, but that interface isn't used anywhere else.

To generate a mock for a nested interface, just use the format `<Struct Name>.<Field Name>` to reference the nested interface, like in the following example:

```go
//go:generate kelpie generate --package github.com/adamconnelly/kelpie/examples --interfaces ConfigService.Encrypter
//go:generate kelpie generate --package github.com/adamconnelly/kelpie/examples --interfaces ConfigService.Storage
type ConfigService struct {
Encrypter interface {
Encrypt(value string) (string, error)
}

Storage interface {
StoreConfigValue(key, value string) error
}
}

func (c *ConfigService) StoreConfig(key, value string) error {
encryptedValue, err := c.Encrypter.Encrypt(value)
if err != nil {
return err
}

return c.Storage.StoreConfigValue(key, encryptedValue)
}
```

We can then use the mocks like this:

```go
func (t *NestedInterfacesTests) Test_ConfigService_StoresEncryptedValue() {
// Arrange
encrypterMock := encrypter.NewMock()
storageMock := storage.NewMock()

encrypterMock.Setup(encrypter.Encrypt("unencrypted").Return("encrypted", nil))

configService := &ConfigService{
Encrypter: encrypterMock.Instance(),
Storage: storageMock.Instance(),
}

// Act
err := configService.StoreConfig("kelpie.testSecret", "unencrypted")

// Assert
t.NoError(err)
t.True(storageMock.Called(storage.StoreConfigValue("kelpie.testSecret", "encrypted")))
}
```

If you need to mock an interface that's nested inside another struct, just specify the dot-separated path to the interface. For example `MyStruct.NestedField.InterfaceToMock`.

### Interface parameters

Under the hood, Kelpie uses Go generics to allow either the actual parameter type or a Kelpie matcher to be passed in when setting up mocks or verifying expectations. For example, say we have the following method:
Expand Down Expand Up @@ -330,7 +386,7 @@ mock.Setup(secrets.Get(kelpie.Any[context.Context](), "MySecret").Return("SuperS
Kelpie can happily mock interfaces that aren't part of your own source. You don't need to do anything special to mock an "external" interface - just specify the package and interface name you want to mock:
```go
//go:generate go run ../cmd/kelpie generate --package io --interfaces Reader
//go:generate kelpie generate --package io --interfaces Reader

func (t *ExternalTypesTests) Test_CanMockAnExternalType() {
// Arrange
Expand Down
3 changes: 2 additions & 1 deletion cmd/kelpie/mock.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ func({{ template "parameterWithTypeList" .Parameters }}){{ if .Results }} {{ tem
package {{ .PackageName }}

import (
"github.com/adamconnelly/kelpie"
{{- if .AnyMethodsHaveParameters }}
"github.com/adamconnelly/kelpie"{{ end }}
"github.com/adamconnelly/kelpie/mocking"
{{- with .Imports }}
{{ range $i := . }}
Expand Down
140 changes: 140 additions & 0 deletions examples/mocks/doublenestedservice/doublenestedservice.go

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

Loading

0 comments on commit 020011e

Please sign in to comment.