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.
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.goRunning 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.
360 Tech Engineering
Official tech channel of 360, building the most professional technology aggregation platform for the brand.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.