Backend Development 12 min read

Guide to Writing and Running Unit Tests in Go

This article explains the importance of unit testing for reliable software, introduces Go's built‑in testing framework, demonstrates how to write test files, use TestMain, benchmark code, and employ the httptest package for HTTP server testing, all illustrated with complete code examples.

360 Tech Engineering
360 Tech Engineering
360 Tech Engineering
Guide to Writing and Running Unit Tests in Go

The article begins by highlighting the benefits of unit testing, such as early bug detection, reduced development cost, lower code complexity, and documentation of expected behavior, which together improve software quality and maintainability.

Go provides a lightweight testing framework consisting of the go test command and the testing package. Test files must end with _test.go and contain functions named TestXxx (where Xxx starts with an uppercase letter). An example test for strings.Index is shown:

package strings_test

import (
    "strings"
    "testing"
)

func TestIndex(t *testing.T) {
    const s, sep, want = "chicken", "ken", 4
    got := strings.Index(s, sep)
    if got != want {
        t.Errorf("Index(%q,%q) = %v; want %v", s, sep, got, want)
    }
}

Test rules include placing the test file in the same package as the code under test, using TestMain(m *testing.M) for setup/teardown, and writing benchmark functions ( func BenchmarkXxx(b *testing.B) ) with a B type for iteration control. A typical project layout is illustrated:

project
|-main.go
|-main_test.go
| -api
|   |-route.go
|   |-route_test.go
|   |-handlers.go
|   `-handlers_test.go
|-storage.go`
|-storage_test.go

Running tests is done with go test (default package) or go test -v for verbose output, and can target all modules (e.g., go test ./... ) or the standard library ( go test std ).

The net/http/httptest package enables HTTP testing. It provides httptest.Server for creating a temporary server and httptest.ResponseRecorder for capturing handler responses. Example of creating a test server:

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello, client")
}))
defer ts.Close()

res, err := http.Get(ts.URL)
if err != nil { log.Fatal(err) }

greeting, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil { log.Fatal(err) }

fmt.Printf("%s", greeting)

Example of using ResponseRecorder to test a handler:

handler := func(w http.ResponseWriter, r *http.Request) {
    http.Error(w, "something failed", http.StatusInternalServerError)
}

req, err := http.NewRequest("GET", "http://example.com/foo", nil)
if err != nil { log.Fatal(err) }

w := httptest.NewRecorder()
handler(w, req)

fmt.Printf("%d - %s", w.Code, w.Body.String())

Key structures from the httptest package are also shown, such as type Server struct { Listener net.Listener; TLS *tls.Config; Config *http.Server } and type ResponseRecorder struct { Code int; HeaderMap http.Header; Body *bytes.Buffer; Flushed bool } , illustrating how they store server and response state for assertions.

Code CoverageTestingGobenchmarkhttptesttest-mainUnit Test
360 Tech Engineering
Written by

360 Tech Engineering

Official tech channel of 360, building the most professional technology aggregation platform for the brand.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.