apitest

Table-driven tests

Cover many scenarios with one test, and share configuration between tests.

Tables of scenarios

Each case in the table describes the dependencies and the expected response. t.Run gives every case its own name in the output:

func TestGetUser_Table(t *testing.T) {
	tests := []struct {
		name       string
		mocks      []*apitest.Mock
		wantStatus int
		wantBody   string
	}{
		{
			name:       "found",
			mocks:      []*apitest.Mock{getUserMock, getPreferencesMock},
			wantStatus: http.StatusOK,
			wantBody:   `{"id": "1234", "name": "jon", "is_contactable": true}`,
		},
		{
			name: "user not found",
			mocks: []*apitest.Mock{
				apitest.NewMock().
					Get("http://users/api/user/1234").
					RespondWith().
					Status(http.StatusNotFound).
					End(),
			},
			wantStatus: http.StatusNotFound,
			wantBody:   `{"error": "user not found"}`,
		},
		{
			name: "preferences unavailable",
			mocks: []*apitest.Mock{
				getUserMock,
				apitest.NewMock().
					Get("http://preferences/api/preferences/1234").
					RespondWith().
					Status(http.StatusServiceUnavailable).
					End(),
			},
			wantStatus: http.StatusBadGateway,
			wantBody:   `{"error": "upstream error"}`,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			apitest.New(tt.name).
				Mocks(tt.mocks...).
				Handler(newUserAPI()).
				Get("/user/1234").
				Expect(t).
				Status(tt.wantStatus).
				Body(tt.wantBody).
				End()
		})
	}
}

Passing the case name to apitest.New also names its sequence diagram, when reporting is on.

Parallel subtests and mocks

Mocks replace http.DefaultTransport for the whole process. Only call t.Parallel() in mocked subtests when each one uses its own client. See parallel tests.

Shared configuration

Every configuration method returns the *apitest.APITest, so a helper can set up the handler, mocks, reporting and anything else the tests have in common:

apiTest := func(name string) *apitest.APITest {
	return apitest.New(name).
		Handler(newUserAPI()).
		Mocks(getUserMock, getPreferencesMock)
}

apiTest("gets a user").
	Get("/user/1234").
	Expect(t).
	Status(http.StatusOK).
	End()

Tests then add only what is specific to them. A package level helper works the same way, as in reporting every test.

On this page