wire: add an example and document how to use wire with mocks (google/go-cloud#488)
This commit is contained in:
committed by
Ross Light
parent
32c3dc8578
commit
2c50843322
21
README.md
21
README.md
@@ -453,3 +453,24 @@ You may not:
|
|||||||
`*Greeter` will be broken.
|
`*Greeter` will be broken.
|
||||||
- Add a provider for `io.Writer` to `GreeterSet`. Injectors might already have
|
- Add a provider for `io.Writer` to `GreeterSet`. Injectors might already have
|
||||||
a provider for `io.Writer` which might conflict with this one.
|
a provider for `io.Writer` which might conflict with this one.
|
||||||
|
|
||||||
|
### Mocking
|
||||||
|
|
||||||
|
There are two approaches for creating an injected app with mocked dependencies.
|
||||||
|
Examples of both approaches are shown
|
||||||
|
[here](https://github.com/google/go-cloud/tree/master/wire/internal/wire/testdata/ExampleWithMocks/foo).
|
||||||
|
|
||||||
|
#### Approach A: Pass mocks to the injector
|
||||||
|
|
||||||
|
Create a test-only injector that takes all of the mocks as arguments; the
|
||||||
|
argument types must be the interface types the mocks are mocking. `wire.Build`
|
||||||
|
can't include providers for the mocked dependencies without creating conflicts,
|
||||||
|
so if you're using provider set(s) you will need to define one that doesn't
|
||||||
|
include the mocked types.
|
||||||
|
|
||||||
|
#### Approach B: Return the mocks from the injector
|
||||||
|
|
||||||
|
Create a new struct that includes the app plus all of the dependencies you want
|
||||||
|
to mock. Create a test-only injector that returns this struct, give it providers
|
||||||
|
for the concrete mock types, and use `wire.Bind` to tell Wire that the
|
||||||
|
concrete mock types should be used to fulfill the appropriate interface.
|
||||||
|
|||||||
120
internal/wire/testdata/ExampleWithMocks/foo/foo.go
vendored
Normal file
120
internal/wire/testdata/ExampleWithMocks/foo/foo.go
vendored
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
// Copyright 2018 The Go Cloud Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// This test demonstrates how to use mocks with wire.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/go-cloud/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Create a "real" greeter.
|
||||||
|
// Greet() will include the real current time, so elide it for repeatable
|
||||||
|
// tests.
|
||||||
|
fmt.Printf("Real time greeting: %s [current time elided]\n", initApp().Greet()[0:15])
|
||||||
|
|
||||||
|
// There are two approaches for creating an app with mocks.
|
||||||
|
|
||||||
|
// Approach A: create the mocks manually, and pass them to an injector.
|
||||||
|
// This approach is useful if you need to prime the mocks beforehand.
|
||||||
|
fmt.Println("Approach A")
|
||||||
|
mt := newMockTimer()
|
||||||
|
mockedApp := initMockedAppFromArgs(mt)
|
||||||
|
fmt.Println(mockedApp.Greet()) // prints greeting with time = zero time
|
||||||
|
mt.T = mt.T.AddDate(1999, 0, 0)
|
||||||
|
fmt.Println(mockedApp.Greet()) // prints greeting with time = year 2000
|
||||||
|
|
||||||
|
// Approach B: allow the injector to create the mocks, and return a struct
|
||||||
|
// that includes the resulting app plus the mocks.
|
||||||
|
fmt.Println("Approach B")
|
||||||
|
appWithMocks := initMockedApp()
|
||||||
|
fmt.Println(appWithMocks.app.Greet()) // prints greeting with time = zero time
|
||||||
|
appWithMocks.mt.T = appWithMocks.mt.T.AddDate(999, 0, 0)
|
||||||
|
fmt.Println(appWithMocks.app.Greet()) // prints greeting with time = year 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
// appSet is a provider set for creating a real app.
|
||||||
|
var appSet = wire.NewSet(
|
||||||
|
app{},
|
||||||
|
greeter{},
|
||||||
|
wire.InterfaceValue(new(timer), realTime{}),
|
||||||
|
)
|
||||||
|
|
||||||
|
// appSetWithoutMocks is a provider set for creating an app with mocked
|
||||||
|
// dependencies. The mocked dependencies are omitted and must be provided as
|
||||||
|
// arguments to the injector.
|
||||||
|
// It is used for Approach A.
|
||||||
|
var appSetWithoutMocks = wire.NewSet(
|
||||||
|
app{},
|
||||||
|
greeter{},
|
||||||
|
)
|
||||||
|
|
||||||
|
// mockAppSet is a provider set for creating a mocked app, including the mocked
|
||||||
|
// dependencies.
|
||||||
|
// It is used for Approach B.
|
||||||
|
var mockAppSet = wire.NewSet(
|
||||||
|
app{},
|
||||||
|
greeter{},
|
||||||
|
appWithMocks{},
|
||||||
|
// For each mocked dependency, add a provider and use wire.Bind to bind
|
||||||
|
// the concrete type to the relevant interface.
|
||||||
|
newMockTimer,
|
||||||
|
wire.Bind(new(timer), new(mockTimer)),
|
||||||
|
)
|
||||||
|
|
||||||
|
type timer interface {
|
||||||
|
Now() time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// realTime implements timer with the real time.
|
||||||
|
type realTime struct{}
|
||||||
|
|
||||||
|
func (realTime) Now() time.Time { return time.Now() }
|
||||||
|
|
||||||
|
// mockTimer implements timer using a mocked time.
|
||||||
|
type mockTimer struct {
|
||||||
|
T time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMockTimer() *mockTimer { return &mockTimer{} }
|
||||||
|
func (m *mockTimer) Now() time.Time { return m.T }
|
||||||
|
|
||||||
|
// greeter issues greetings with the time provided by T.
|
||||||
|
type greeter struct {
|
||||||
|
T timer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g greeter) Greet() string {
|
||||||
|
return fmt.Sprintf("Good day! It is %v", g.T.Now())
|
||||||
|
}
|
||||||
|
|
||||||
|
type app struct {
|
||||||
|
g greeter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a app) Greet() string {
|
||||||
|
return a.g.Greet()
|
||||||
|
}
|
||||||
|
|
||||||
|
// appWithMocks is used for Approach B, to return the app plus its mocked
|
||||||
|
// dependencies.
|
||||||
|
type appWithMocks struct {
|
||||||
|
app app
|
||||||
|
mt *mockTimer
|
||||||
|
}
|
||||||
42
internal/wire/testdata/ExampleWithMocks/foo/wire.go
vendored
Normal file
42
internal/wire/testdata/ExampleWithMocks/foo/wire.go
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// Copyright 2018 The Go Cloud Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//+build wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/google/go-cloud/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
// initApp returns a real app.
|
||||||
|
func initApp() *app {
|
||||||
|
wire.Build(appSet)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// initMockedAppFromArgs returns an app with mocked dependencies provided via
|
||||||
|
// arguments (Approach A). Note that the argument's type is the interface
|
||||||
|
// type (timer), but the concrete mock type should be passed.
|
||||||
|
func initMockedAppFromArgs(mt timer) *app {
|
||||||
|
wire.Build(appSetWithoutMocks)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// initMockedApp returns an app with its mocked dependencies, created
|
||||||
|
// via providers (Approach B).
|
||||||
|
func initMockedApp() *appWithMocks {
|
||||||
|
wire.Build(mockAppSet)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
1
internal/wire/testdata/ExampleWithMocks/pkg
vendored
Normal file
1
internal/wire/testdata/ExampleWithMocks/pkg
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
example.com/foo
|
||||||
7
internal/wire/testdata/ExampleWithMocks/want/program_out.txt
vendored
Normal file
7
internal/wire/testdata/ExampleWithMocks/want/program_out.txt
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Real time greeting: Good day! It is [current time elided]
|
||||||
|
Approach A
|
||||||
|
Good day! It is 0001-01-01 00:00:00 +0000 UTC
|
||||||
|
Good day! It is 2000-01-01 00:00:00 +0000 UTC
|
||||||
|
Approach B
|
||||||
|
Good day! It is 0001-01-01 00:00:00 +0000 UTC
|
||||||
|
Good day! It is 1000-01-01 00:00:00 +0000 UTC
|
||||||
48
internal/wire/testdata/ExampleWithMocks/want/wire_gen.go
vendored
Normal file
48
internal/wire/testdata/ExampleWithMocks/want/wire_gen.go
vendored
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// Code generated by Wire. DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:generate wire
|
||||||
|
//+build !wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
// Injectors from wire.go:
|
||||||
|
|
||||||
|
func initApp() *app {
|
||||||
|
mainTimer := _wireRealTimeValue
|
||||||
|
mainGreeter := greeter{
|
||||||
|
T: mainTimer,
|
||||||
|
}
|
||||||
|
mainApp := &app{
|
||||||
|
g: mainGreeter,
|
||||||
|
}
|
||||||
|
return mainApp
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_wireRealTimeValue = realTime{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func initMockedAppFromArgs(mt timer) *app {
|
||||||
|
mainGreeter := greeter{
|
||||||
|
T: mt,
|
||||||
|
}
|
||||||
|
mainApp := &app{
|
||||||
|
g: mainGreeter,
|
||||||
|
}
|
||||||
|
return mainApp
|
||||||
|
}
|
||||||
|
|
||||||
|
func initMockedApp() *appWithMocks {
|
||||||
|
mainMockTimer := newMockTimer()
|
||||||
|
mainGreeter := greeter{
|
||||||
|
T: mainMockTimer,
|
||||||
|
}
|
||||||
|
mainApp := app{
|
||||||
|
g: mainGreeter,
|
||||||
|
}
|
||||||
|
mainAppWithMocks := &appWithMocks{
|
||||||
|
app: mainApp,
|
||||||
|
mt: mainMockTimer,
|
||||||
|
}
|
||||||
|
return mainAppWithMocks
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user