简体   繁体   中英

Revel Dependency Injection

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?

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.

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)
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM