Skip to content

Commit

Permalink
Merge pull request #1 from tkrop/initial-checkin
Browse files Browse the repository at this point in the history
feat: initial checkin (#1)
  • Loading branch information
Tronje Krop authored Oct 13, 2022
2 parents f55a127 + 4bde622 commit 0b89729
Show file tree
Hide file tree
Showing 12 changed files with 2,054 additions and 0 deletions.
542 changes: 542 additions & 0 deletions Makefile

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions Makefile.defs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Setup environment for all run-targets (to be modified)
define run-setup
true
endef

# Define variables for all run-targets (to be modified)
define run-vars

endef

# Define variables for local run-targets (to be modified)
define run-vars-local

endef

# Define variables for image run-targets (to be modified)
define run-vars-image

endef

# Define aws localstack setup (to be modified).
define run-aws-setup
true
endef
7 changes: 7 additions & 0 deletions Makefile.vars
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Setup required targets before testing.
#TEST_DEPS := run-db
# Setup required targets before running commands.
#RUN_DEPS := run-db

IMAGE_PUSH ?= never
TEST_TIMEOUT := 5s
193 changes: 193 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
# Usage patterns of go-base/mock

This package contains a small extension library to handle mock call setup in a
standardized way. Unfortunately, we had to sacrifice type-safety to allow for
chaining mock calls in an arbitrary way during setup. Anyhow, in tests runtime
validation is likely a sufficient strategy.


## Generic mock setup pattern

To setup a generic mock handler for any number of mocks, one can simply use the
following template to setup an arbitrary system under test.

```go
func SetupTestUnit(
t *gomock.TestReporter,
mockSetup mock.SetupFunc,
...
) (*Unit, *Mocks) {
mocks := mock.NewMock(t).Expect(mockSetup)

unit := NewUnitService(
mock.Get(mocks, NewServiceMock)
).(*Unit)

return unit, mocks
}
```

**Note:** The `mock.Get(mocks, NewServiceMock)` is the standard pattern to
request an existing or new mock instance from the mock handler.


## Generic mock service call pattern

Now we need to define the mock service calls that follow a primitive, common
coding and naming pattern, that may be supported by code generation in the
future.

```go
func ServiceCall(input..., output..., error) mock.SetupFunc {
return func(mocks *Mocks) any {
mocks.WaitGroup().Add(1)
return Get(mocks, NewServiceMock).EXPECT().
ServiceCall(input...).Return(output..., error).Times(1).
Do(func(input... interface{}) {
defer mocks.WaitGroup().Done()
})

}
}
```

For simplicity the pattern combines regular as well as error behavior and is
prepared to handle tests with detached *goroutines*, however, this code can
be left out for further simplification.

For detached *goroutines*, i.e. functions that do not communicate with the
test, the mock handler provides a `WaitGroup` to registers expected mock calls
using `mocks.WaitGroup().Add(<times>)` and notifying the occurrence by calling
`mocks.WaitGroup().Done()` in as a mock callback function registered for the
match via `Do(<func>)`. The test waits for the detached *goroutines* to finish
by calling `mocks.WaitGroup().Wait()`.

A static series of mock service calls can now simply expressed by chaining the
mock service calls as follows using `mock.Chain` and while defining a new mock
call setup function:

```go
func ServiceCallChain(input..., output..., error) mock.SetupFunc {
return func(mocks *Mocks) any {
return mock.Chain(
ServiceCallA(input...),
ServiceCallB(input...),
...
}
}
```


## Generic mock ordering patterns

With the above preparations for mocking service calls we can now define the
*mock setup* easily using the following ordering methods:

* `Chain` allows to create an ordered chain of mock calls that can be combined
with other setup methods that defermine the predecessors and successor mock
calls.

* `Parallel` allows to creates an unordered set of mock calls that can be
combined with other setup methods that determine the predecessor and
successor mock calls.

* `Setup` allows to create an unordered detached set of mock calls that creates
no relation to predecessors and successors it was defined with.

Beside this simple (un-)ordering methods there are two further methods for
completeness, that allow to control how predecessors and successors are used
to setup ordering conditions:

* `Sub` allows to define a sub-set or sub-chain of elements in `Parallel` and
`Chain` as predecessor and successor context for further combination.

* `Detach` allows to detach an element from the predecessor context (`Head`),
from the successor context (`Tail`), or from both which is used in `Setup`.

The application of these two functions may be a bit more complex but still
follows the intuition.


## Generic parameterized test pattern

The ordering methods and the mock service call setups can now be used to define
the mock call expectations, in a parameter setup as follows to show the most
common use cases:

```go
var testUnitCallParams = map[string]struct {
mockSetup mock.SetupFunc
...
expect* *model.*
expectError error
}{
"single mock setup": {
mockSetup: ServiceCall(...),
}
"chain mock setup": {
mockSetup: mock.Chain(
ServiceCallA(...),
ServiceCallB(...),
...
)
}
"nested chain mock setup": {
mockSetup: mock.Chain(
ServiceCallA(...),
mock.Chain(
ServiceCallA(...),
ServiceCallB(...),
...
),
ServiceCallB(...),
...
)
}
"parallel chain mock setup": {
mockSetup: mock.Parallel(
ServiceCallA(...),
mock.Chain(
ServiceCallB(...),
ServiceCallC(...),
...
),
mock.Chain(
ServiceCallD(...),
ServiceCallE(...),
...
),
...
)
}
...
}
```

This test parameter setup can now be use for a parameterized unit test using
the following common pattern:

```go
func TestUnitCall(t *testing.T) {
for message, param := range testUnitCallParams {
t.Run(message, func(t *testing.T) {
require.NotEmpty(t, message)

//Given
unit, mocks := SetupTestUnit(t, param.mockSetup)

//When
result, err := unit.UnitCall(...)

mock.WaitGroup().Wait()

//Then
if param.expectError != nil {
assert.Equal(t, param.expectError, err)
} else {
require.NoError(t, err)
}
assert.Equal(t, param.expect*, result)
})
}
}
```
99 changes: 99 additions & 0 deletions build/test-unit.cover
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
mode: atomic
github.com/tkrop/testing/mock/mock.go:29.37,30.11 1 8
github.com/tkrop/testing/mock/mock.go:31.12,32.16 1 0
github.com/tkrop/testing/mock/mock.go:33.12,34.16 1 2
github.com/tkrop/testing/mock/mock.go:35.12,36.16 1 2
github.com/tkrop/testing/mock/mock.go:37.12,38.16 1 2
github.com/tkrop/testing/mock/mock.go:39.10,40.19 1 2
github.com/tkrop/testing/mock/mock.go:78.44,84.2 1 863
github.com/tkrop/testing/mock/mock.go:87.54,88.20 1 863
github.com/tkrop/testing/mock/mock.go:91.2,91.14 1 855
github.com/tkrop/testing/mock/mock.go:88.20,90.3 1 863
github.com/tkrop/testing/mock/mock.go:96.49,98.2 1 5513
github.com/tkrop/testing/mock/mock.go:102.64,105.24 3 5752
github.com/tkrop/testing/mock/mock.go:108.2,110.19 3 862
github.com/tkrop/testing/mock/mock.go:105.24,107.3 1 4890
github.com/tkrop/testing/mock/mock.go:118.55,119.27 1 975
github.com/tkrop/testing/mock/mock.go:119.27,120.30 1 975
github.com/tkrop/testing/mock/mock.go:123.3,123.13 1 966
github.com/tkrop/testing/mock/mock.go:120.30,122.4 1 1071
github.com/tkrop/testing/mock/mock.go:131.57,132.27 1 1639
github.com/tkrop/testing/mock/mock.go:132.27,134.34 2 1639
github.com/tkrop/testing/mock/mock.go:137.3,137.15 1 1638
github.com/tkrop/testing/mock/mock.go:134.34,136.4 1 4123
github.com/tkrop/testing/mock/mock.go:150.60,151.27 1 1456
github.com/tkrop/testing/mock/mock.go:151.27,153.34 2 1456
github.com/tkrop/testing/mock/mock.go:156.3,156.15 1 1456
github.com/tkrop/testing/mock/mock.go:153.34,155.4 1 2911
github.com/tkrop/testing/mock/mock.go:163.71,164.27 1 100
github.com/tkrop/testing/mock/mock.go:164.27,165.15 1 100
github.com/tkrop/testing/mock/mock.go:166.13,167.23 1 24
github.com/tkrop/testing/mock/mock.go:168.13,169.37 1 25
github.com/tkrop/testing/mock/mock.go:170.13,171.37 1 25
github.com/tkrop/testing/mock/mock.go:172.13,173.37 1 25
github.com/tkrop/testing/mock/mock.go:174.11,175.30 1 1
github.com/tkrop/testing/mock/mock.go:185.65,186.27 1 64
github.com/tkrop/testing/mock/mock.go:186.27,188.37 2 64
github.com/tkrop/testing/mock/mock.go:189.14,191.46 2 15
github.com/tkrop/testing/mock/mock.go:192.16,194.39 2 15
github.com/tkrop/testing/mock/mock.go:195.19,197.39 2 15
github.com/tkrop/testing/mock/mock.go:198.21,199.36 1 1
github.com/tkrop/testing/mock/mock.go:200.21,201.36 1 1
github.com/tkrop/testing/mock/mock.go:202.21,203.36 1 1
github.com/tkrop/testing/mock/mock.go:204.12,205.14 1 15
github.com/tkrop/testing/mock/mock.go:206.11,207.27 1 1
github.com/tkrop/testing/mock/mock.go:216.54,219.15 3 53
github.com/tkrop/testing/mock/mock.go:224.2,224.20 1 33
github.com/tkrop/testing/mock/mock.go:219.15,221.3 1 1
github.com/tkrop/testing/mock/mock.go:221.8,221.22 1 52
github.com/tkrop/testing/mock/mock.go:221.22,223.3 1 19
github.com/tkrop/testing/mock/mock.go:229.44,231.13 2 106
github.com/tkrop/testing/mock/mock.go:240.2,240.16 1 1
github.com/tkrop/testing/mock/mock.go:231.13,233.14 2 22
github.com/tkrop/testing/mock/mock.go:236.3,236.13 1 21
github.com/tkrop/testing/mock/mock.go:233.14,235.4 1 1
github.com/tkrop/testing/mock/mock.go:237.8,237.22 1 84
github.com/tkrop/testing/mock/mock.go:237.22,239.3 1 83
github.com/tkrop/testing/mock/mock.go:247.53,248.28 1 4123
github.com/tkrop/testing/mock/mock.go:267.2,267.14 1 4122
github.com/tkrop/testing/mock/mock.go:248.28,249.35 1 4123
github.com/tkrop/testing/mock/mock.go:250.14,251.31 1 3252
github.com/tkrop/testing/mock/mock.go:252.16,253.34 1 24
github.com/tkrop/testing/mock/mock.go:254.19,255.31 1 735
github.com/tkrop/testing/mock/mock.go:256.21,257.31 1 24
github.com/tkrop/testing/mock/mock.go:258.21,259.31 1 24
github.com/tkrop/testing/mock/mock.go:260.21,261.31 1 24
github.com/tkrop/testing/mock/mock.go:262.12,262.12 0 39
github.com/tkrop/testing/mock/mock.go:263.11,264.26 1 1
github.com/tkrop/testing/mock/mock.go:273.49,274.34 1 9245
github.com/tkrop/testing/mock/mock.go:275.13,276.36 1 4950
github.com/tkrop/testing/mock/mock.go:277.18,278.40 1 1471
github.com/tkrop/testing/mock/mock.go:279.15,280.37 1 1614
github.com/tkrop/testing/mock/mock.go:281.20,282.42 1 1088
github.com/tkrop/testing/mock/mock.go:283.20,284.42 1 24
github.com/tkrop/testing/mock/mock.go:285.20,286.42 1 24
github.com/tkrop/testing/mock/mock.go:287.11,288.17 1 72
github.com/tkrop/testing/mock/mock.go:289.10,290.25 1 2
github.com/tkrop/testing/mock/mock.go:296.55,297.23 1 4950
github.com/tkrop/testing/mock/mock.go:304.2,304.22 1 4950
github.com/tkrop/testing/mock/mock.go:297.23,298.34 1 3852
github.com/tkrop/testing/mock/mock.go:298.34,299.22 1 5331
github.com/tkrop/testing/mock/mock.go:299.22,301.5 1 5331
github.com/tkrop/testing/mock/mock.go:309.59,310.29 1 1614
github.com/tkrop/testing/mock/mock.go:313.2,313.16 1 1614
github.com/tkrop/testing/mock/mock.go:310.29,312.3 1 4059
github.com/tkrop/testing/mock/mock.go:319.65,321.29 2 1471
github.com/tkrop/testing/mock/mock.go:324.2,324.17 1 1470
github.com/tkrop/testing/mock/mock.go:321.29,323.3 1 2941
github.com/tkrop/testing/mock/mock.go:329.69,330.29 1 1088
github.com/tkrop/testing/mock/mock.go:333.2,333.16 1 1086
github.com/tkrop/testing/mock/mock.go:330.29,332.3 1 1088
github.com/tkrop/testing/mock/mock.go:339.69,340.29 1 24
github.com/tkrop/testing/mock/mock.go:343.2,343.16 1 24
github.com/tkrop/testing/mock/mock.go:340.29,342.3 1 24
github.com/tkrop/testing/mock/mock.go:349.69,350.29 1 24
github.com/tkrop/testing/mock/mock.go:353.2,353.16 1 24
github.com/tkrop/testing/mock/mock.go:350.29,352.3 1 24
github.com/tkrop/testing/mock/mock.go:358.32,361.2 1 8
github.com/tkrop/testing/mock/mock.go:364.43,366.2 1 2
github.com/tkrop/testing/mock/mock.go:369.49,371.2 1 6
14 changes: 14 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module github.com/tkrop/testing

go 1.19

require (
github.com/golang/mock v1.6.0
github.com/stretchr/testify v1.8.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
40 changes: 40 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading

0 comments on commit 0b89729

Please sign in to comment.