Home > Article > Backend Development > How to use dependency injection for unit testing in Golang?
Using dependency injection (DI) in Golang unit testing can isolate the code to be tested and simplify test setup and maintenance. Popular DI libraries include wire and go-inject, which can generate dependency stubs or mocks for testing. The steps of DI testing include setting up dependencies, setting up test cases and asserting results. An example of using DI to test an HTTP request handling function shows how easy it is to isolate and test code without actual dependencies or communication.
How to use dependency injection for unit testing in Golang
Dependency Injection (DI) is a design pattern that allows You provide an object with its dependencies without explicitly creating them. In unit testing, DI can help you isolate the code you want to test and make the tests easier to set up and maintain.
DI in Golang
There are many popular DI libraries in Golang, the most famous of which is [wire](https://github.com/google/ wire) and [go-inject](https://github.com/go-inject/go-inject). These libraries work by generating stubs or mocks that can be used as dependencies in tests.
Set up DI testing
Here's how to set up DI unit testing using wire:
import ( "context" "testing" "github.com/google/go-cmp/cmp" ) // Interface we want to test. type Greeter interface { Greet(ctx context.Context, name string) (string, error) } // Implementation we want to test. type DefaultGreeter struct{} func (g DefaultGreeter) Greet(ctx context.Context, name string) (string, error) { return "Hello, " + name, nil } func TestGreeter_Greet(t *testing.T) { type Fields struct { greeter Greeter } wire.Build(Fields{ greeter: (*DefaultGreeter)(nil), }) cases := []struct { setup func(t *testing.T, fields Fields) expected *string wantErr bool }{ { expected: String("Hello, Bob"), }, } for _, tc := range cases { tc := tc // capture range variable t.Run(testName, func(t *testing.T) { t.Parallel() fields := Fields{} tc.setup(t, fields) result, err := fields.greeter.Greet(context.Background(), "Bob") if (err != nil) != tc.wantErr { t.Fatalf("error = %v, wantErr = %v", err, tc.wantErr) } if tc.wantErr { return } if diff := cmp.Diff(*tc.expected, result); diff != "" { t.Fatalf("result mismatch (-want +got):\n%s", diff) } }) } }
Using DI for testing
In the above test, we use wire.Build
to generate an instance of a Fields
structure that contains the dependency stubs to be used for testing. We can then set up the test case and assert the results as usual.
Practical Case
The following is how to use DI to unit test a function that handles HTTP requests:
import ( "net/http" "net/http/httptest" "testing" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" "mypkg/handlers" ) // Interface we want to test. type UserService interface { GetUser(id int) (*User, error) } // Implementation we want to test. type DefaultUserService struct{} func (s DefaultUserService) GetUser(id int) (*User, error) { return &User{ID: id, Name: "Bob"}, nil } type Request struct { UserService UserService } func (r Request) ServeHTTP(w http.ResponseWriter, req *http.Request) { id, err := strconv.Atoi(mux.Vars(req)["id"]) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } user, err := r.UserService.GetUser(id) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } fmt.Fprintf(w, "%s", user.Name) } func TestHandler_GetUser(t *testing.T) { r := &Request{} type Fields struct { userService UserService } wire.Build(Fields{ userService: (*DefaultUserService)(nil), }) cases := []struct { name string id int body string want string }{ { body: `{"body":""}`, want: `Bob`, }, { id: 2, body: `{"body":""}`, want: `Bob`, }, } for _, tc := range cases { tc := tc // capture range variable t.Run(tc.name, func(t *testing.T) { req, _ := http.NewRequest("GET", "/", bytes.NewBuffer([]byte(tc.body))) if tc.id != 0 { req = mux.SetURLVars(req, map[string]string{"id": strconv.Itoa(tc.id)}) } rr := httptest.NewRecorder() handler := http.HandlerFunc(r.ServeHTTP) handler.ServeHTTP(rr, req) assert.Equal(t, tc.want, rr.Body.String()) }) } }
By using DI and stubs, we can easily Isolate and test the GetUser
function without involving actual database or HTTP requests.
The above is the detailed content of How to use dependency injection for unit testing in Golang?. For more information, please follow other related articles on the PHP Chinese website!