Dependency Injection in Golang

Phase 1: “Jam everything in main()”

package main

import (
"fmt"
"net/http"
"io/ioutil"
)

func main() {
client := http.Client{}
response, err := client.Get("http://google.com")

if err != nil {
panic(err)
}

if response == nil {
fmt.Println("Received an empty response!")
return
}

body, err := ioutil.ReadAll(response.Body)

if err != nil {
fmt.Println("Couldn't read body of response!")
return
}

fmt.Println(string(body))
}
go run main.go

Phase 2: “Break it out into functions”

package main

import (
"fmt"
"net/http"
"io/ioutil"
"flag"
)

var (
url string
)

func init() {
flag.StringVar(&url, "url", "http://google.com", "Which URL do we want to parse?")
flag.Parse()
}

func main() {
err := send(url)

if err != nil {
panic(err)
}
}

func send(link string) error {
client := http.Client{}
response, err := client.Get(link)

if err != nil {
return err
}

if response == nil {
return err
}

body, err := ioutil.ReadAll(response.Body)

if err != nil {
return err
}

fmt.Println(string(body))
return nil
}
go run main.go -url http://google.com

Phase 3: “Dependency injection is your friend”

type HttpClient interface {
Get(string) (*http.Response, error)
}
package main

import (
"fmt"
"net/http"
"io/ioutil"
"flag"
)

var (
url string
)

type HttpClient interface {
Get(string) (*http.Response, error)
}

func init() {
flag.StringVar(&url, "url", "http://google.com", "Which URL do we want to parse?")
flag.Parse()
}

func main() {
client := &http.Client{}
err := send(client, url)

if err != nil {
panic(err)
}
}

func send(client HttpClient, link string) error {
response, err := client.Get(link)

//... truncated for brevity
}
package main

import (
"bytes"
"io/ioutil"
"net/http"
"testing"
)

type MockHttpClient struct {}

func (m *MockHttpClient) Get(url string) (*http.Response, error) {
response := &http.Response{
Body: ioutil.NopCloser(bytes.NewBuffer([]byte("Test Response"))),
}

return response, nil
}

func TestSendWithValidResponse(t *testing.T) {
httpClient := &MockHttpClient{}
err := send(httpClient, "IT_JUST_WORKS!")

if err != nil {
t.Errorf("Shouldn't have received an error with a valid MockHttpClient, got %s", err)
}
}
type MockHttpClient struct {}
func (m *MockHttpClient) Get(url string) (*http.Response, error) {
response := &http.Response{
Body: ioutil.NopCloser(bytes.NewBuffer([]byte("Test Response"))),
}

return response, nil
}
func TestSendWithValidResponse(t *testing.T) {
httpClient := &MockHttpClient{}
err := send(httpClient, "IT_JUST_WORKS!")

if err != nil {
t.Errorf("Shouldn't have received an error with a valid MockHttpClient, got %s", err)
}
}
go test -v
=== RUN   TestSendWithValidResponse
Test Response
--- PASS: TestSendWithValidResponse (0.00s)
PASS
ok _ 0.008s

--

--

--

Software Engineer at Sidecar Interactive

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Zach Kauffman

Zach Kauffman

Software Engineer at Sidecar Interactive

More from Medium

Go Project Structure

Open Card Catalog

A TL;DR guide to MVC frameworks

gRPC proto files Best Practices

Implement Domain-Driven Design (DDD) in Golang