Simple Go dependency injection (Part 1)
Aug 1, 2018 · 3 minute read · CommentsSimple Go dependency injection (Part 1)
So you wrote a cool application that depends on some 3rd party code that accesses a DB and you want to make sure your code works a you want but that damn dependency makes it so that unit tests are not possible…. Think again! Below is simple app that calls fmt.Printf to print our greeting on the screen. In this simple example we want to unit test Greet to make sure the empty name produces the Hello World!!! message, but we need to do this without actually calling the fmt.Printf function.
package main
import (
"fmt"
)
func Greet(name string) {
if name == "" {
fmt.Printf("Hello World!!!")
} else {
fmt.Printf("Hello %s", name)
}
}
func main() {
Greet("")
}
ok so first thing to know is that functions are first class variables in go so we can declare a variable that holds a function, like:
var myAdd func(x, y int) int { return x+y }
myAdd can now be used in your code. Using this concept we can now look at the fmt.Printf function signature and create our own version to use in our code like this:
package main
import (
"fmt"
)
var myPrinf = fmt.Printf
func Greet(name string) {
if name == "" {
myPrinf("Hello World!!!")
} else {
myPrinf("Hello %s", name)
}
}
func main() {
Greet("Tester")
}
Above you can see we declared a new variable and set it to the fmt.Printf function. Now in a unit test we can replace the value of myPrintf with a mocked version that does anything we want. To do that we need to know the complete function signature for fmt.Printf. The signature is: func Printf(format string, a …interface{}) (n int, err error). Once we have that we can write the tests like such:
func TestGreet(t *testing.T) {
var result string
myPrintdf = func(format string, a ...interface{}) (n int, err error) {
result = fmt.Sprintf(format, a...)
n = len(result)
err = nil
return
}
expected := "Hello, Tester"
Greet("Tester")
if expected != result {
t.Errorf("got '%s' want '%s'", result, expected)
}
}
Once run this provides the output:
=== RUN TestGreet
--- PASS: TestGreet (0.00s)
PASS
As an quick exercise try to write the unit test for the empty name version. I hope this helps in your future unit tests. Do not hesitate to ask me any questions on twitter @gocodecloud. In the next couple of days I will be writing an articles about using interfaces for more complex unit testing situations. Happy Going!!!
From here you can move on to Simple Go Dependency Injection Part 2