A test double is a simplified implementation used only during testing. It replaces a real dependency, such as a database or external service, with controlled behavior or data so the code under test can be exercised in isolation. section 11.3.2
In Go, interfaces make substitution natural. HTTP handlers depend on the narrow domain.ArticleReader interface rather than concrete storage, allowing a real BoltStore in production and a small test implementation in tests. section 11.3.2
Choose the double according to the test’s purpose:
- Use a stub when the test needs predetermined data, such as a fixed list of articles. section 11.3.2
- Use a mock when the test must verify interactions, such as whether a method received expected arguments; mocking libraries can support this. section 11.3.2
- Keep the double behind the same interface as the production dependency, preserving interchangeable behavior through the interface contract. section 11.4
Isolating an HTTP handler
Construct Handlers with a test implementation of domain.ArticleReader that returns canned articles. Register the handler on an isolated http.ServeMux, then test query parsing, HTTP method handling, and JSON responses without opening a database or contacting an external service. section 11.3.2
Listing 11.4 HTTP handlers with an injected ArticleReader
package handlers
import (
"encoding/json"
"net/http"
"strconv"
"github.com/YOUR_USERNAME/go-news/domain"
)
type Handlers struct { #1
articles domain.ArticleReader
}
func New(articles domain.ArticleReader) *Handlers { #2
return &Handlers{
articles: articles,
}
}
func (h *Handlers) RegisterRoutes(mux *http.ServeMux) { #3
mux.HandleFunc("/articles", h.articlesHandler)
}
func (h *Handlers) articlesHandler(w http.ResponseWriter, r *http.Request) { #4
if r.Method != http.MethodGet {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
n := 10
if countStr := r.URL.Query().Get("count"); countStr != "" { #5
if count, err := strconv.Atoi(countStr); err == nil && count > 0 {
n = count
}
}
articles := h.articles.GetRecent(n)
w.Header().Set("Content-Type", "application/json") #6
if err := json.NewEncoder(w).Encode(articles); err != nil {
http.Error(w, "failed to encode response", http.StatusInternalServerError)
return
}
}
Use the injected ArticleReader handler listing to trace dependency injection, route registration, method validation, and query handling. section 11.3.2





