Reputation: 801
I'm looking to have my revel controllers use various services, which I mock out for unit tests. I'm new to Go; in C# I'd inject them using dependency injection. Is there a common way to do this in revel?
It seems like the best way I've found is to initialise the real services in the controller's Before() method (possibly using a method solved by wire), and to set the mock versions in the the test's Before() method. Or is there a better way?
Upvotes: 2
Views: 571
Reputation: 69389
I use a filter to inject dependencies.
The filter tests if the controller implements specific interfaces and stuffs the correct dependency in. Here's an example that inserts a database-related dependency:
func DependencyFilter(c *revel.Controller, filterChain []revel.Filter) {
if ctrl, ok := c.AppController.(DataServiceController); ok {
ctrl.SetDataService(<your dependency>)
}
// Different dependencies could be injected here:
//if ctrl, ok := c.AppController.(FooController); ok {
// ctrl.SetFooService(<your dependency>)
//}
// Call the next filter
if len(filterChain) > 0 {
filterChain[0](c, filterChain[1:])
}
}
Where DataServiceController
is:
type DataServiceController interface {
SetDataService(ds services.DataService)
}
I inserted my filter as the penultimate entry in init.go
:
revel.Filters = []revel.Filter{
revel.PanicFilter, // Recover from panics and display an error page instead.
// ...
DependencyFilter, // Add dependencies
revel.ActionInvoker, // Invoke the action.
}
Most of my controllers need the same dependencies, so I have a base controller that they all embed:
type BaseController struct {
*revel.Controller
DataService services.DataService
}
func (c *BaseController) SetDataService(ds services.DataService) {
c.DataService = ds
}
So my concrete controllers look like this:
type Home struct {
BaseController
}
func (c Home) Index() revel.Result {
// ...
}
There might be better ways, but this is my approach.
Upvotes: 3
Reputation: 12875
In fact there's a lot of DI systems for GO. I've searched for several, tried to use and finally have chosen one with some patches for more convenience. Usage is quite easy:
package dependency
import (
"fmt"
"github.com/lisitsky/inject"
)
func init() {
inject.Provide(NewStringer)
}
type stringer struct{}
func (s stringer) String() string {
return "Hello, World"
}
func NewStringer() fmt.Stringer {
return stringer{}
}
On a side accepting dependency (main.go):
package main
import (
"fmt"
"github.com/lisitsky/inject"
_ "github.com/lisitsky/inject/examples/simple/dependency"
)
var (
str fmt.Stringer
)
func main() {
inject.Construct(&str)
fmt.Println("My Stringer is:", str)
}
Also it supports delayed initialization:
func main() {
// define variables to be constructed later
inject.ConstructLater(&str)
// define dependency providers
inject.Provide(NewStringer)
// finalize construction - all DI variables would be initialized at one call
injector.FinishConstruct()
fmt.Println("My Stringer is:", str)
}
Upvotes: 0