As we’ve been building out this API, we’ve covered authentication, logging, Dockerization, and more. But one thing we haven’t discussed yet is testing! If you want your API to be production-ready, adding solid unit tests is crucial. In this post, we’ll go over the basics of unit testing in Go, so you can catch bugs early and ship high-quality code.
Why Unit Testing?
Unit tests help you verify that each part of your codebase works as expected. They’re your first line of defense against bugs, regressions, and other nasty surprises. With Go’s built-in testing library, you can quickly set up tests that:
- Ensure consistent behavior of your functions.
- Make it easier to refactor code without introducing new issues.
- Improve your confidence that everything is working as it should.
Ready to get started? Let’s dive in! 🏊
Step 1: Setting Up a Basic Test
Go’s testing framework is simple and integrated right into the language. You can create a test file by naming it with the _test.go
suffix. Let’s start by testing a simple function in main.go
:
// main.go
package main
func Add(a, b int) int {
return a + b
}
Now, create a file named main_test.go
and add the following code:
// main_test.go
package main
import "testing"
func TestAdd(t *testing.T) {
result := Add(2, 3)
expected := 5
if result != expected {
t.Errorf("Add(2, 3) = %d; want %d", result, expected)
}
}
How It Works:
-
Test Function: Any test function in Go must start with
Test
and accept a*testing.T
parameter. -
Assertion: We check if the result matches our expectation. If it doesn’t, we log an error using
t.Errorf
.
To run the test, just use:
go test
If everything works, you’ll see an ok
message. 🎉
Step 2: Testing HTTP Handlers
Now, let’s write a test for one of our HTTP handlers. We’ll use Go’s httptest
package to create a mock HTTP request and response recorder.
// main_test.go
package main
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestGetBooksHandler(t *testing.T) {
req, err := http.NewRequest("GET", "/books", nil)
if err != nil {
t.Fatal(err)
}
rr := httptest.NewRecorder()
handler := http.HandlerFunc(getBooks)
handler.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK)
}
}
Explanation:
-
httptest.NewRequest: Creates a new HTTP request. This mocks a request to your
/books
endpoint. - httptest.NewRecorder: Mocks an HTTP response. We’ll check this later to see if it matches our expectations.
-
ServeHTTP: Calls our
getBooks
handler with the mock request and recorder.
This way, you can isolate and test your handlers without having to spin up a full server. 🚀
Step 3: Running Tests with Coverage
Go has a built-in way to check test coverage. To see what percentage of your code is covered by tests, you can run:
go test -cover
For more detailed coverage, generate an HTML report:
go test -coverprofile=coverage.out
go tool cover -html=coverage.out
Open the generated HTML file to visualize which parts of your code are covered. It’s a fantastic way to see where you may need additional testing.
Step 4: Mocking External Dependencies
When testing functions that depend on external services (e.g., database or external API calls), you can use interfaces to mock those dependencies.
// Define a simple interface for our database
type Database interface {
GetBooks() ([]Book, error)
}
// Implement a mock database
type MockDatabase struct{}
func (m MockDatabase) GetBooks() ([]Book, error) {
return []Book{{Title: "Mock Book"}}, nil
}
By using interfaces, you can replace the actual dependency with your mock during testing. This keeps your tests fast, isolated, and repeatable.
What’s Next?
Now that you’ve started building unit tests, try adding tests to other parts of your API! 🚀 Next week, we’ll look at integrating a CI/CD pipeline so these tests can run automatically with every change. Stay tuned!
Question for You: What’s your favorite testing tool or technique? Drop a comment below—I’d love to hear how other Go devs approach testing!
With these basics, you’re well on your way to writing solid tests that make your Go API more reliable. For more testing tips and advanced techniques, stay tuned for future posts. Happy testing! 🧪
Top comments (0)