Go Testing 2026: Unit Test, Mock e Domande per Colloqui Tecnici
Una guida completa alle pratiche di testing in Go nel 2026, inclusi unit test con il pacchetto testing, mocking con gomock e domande frequenti nei colloqui tecnici.

Il testing in Go si basa sul pacchetto standard testing, distribuito con ogni installazione di Go e che non richiede dipendenze esterne. A differenza dei framework in altri linguaggi, l'approccio di Go privilegia la semplicità: i file di test si trovano accanto al codice di produzione, le funzioni di test seguono una convenzione di denominazione, e il comando go test gestisce la scoperta e l'esecuzione.
Candidati che scrivono test table-driven, utilizzano le interfacce per la dependency injection e comprendono quando il mocking aggiunge valore rispetto a quando aggiunge solo rumore.
Scrivere Unit Test con il pacchetto testing
Ogni file di test termina con _test.go e risiede nello stesso package del codice da testare. Le funzioni di test iniziano con Test seguito da un nome con iniziale maiuscola. Il parametro *testing.T fornisce metodi per segnalare i fallimenti.
package calculator
import "testing"
func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Errorf("Add(2, 3) = %d; want 5", result)
}
}L'esecuzione di go test ./... esegue tutti i test nel modulo corrente. Il flag -v mostra i nomi dei singoli test e il loro stato di superamento/fallimento.
Test Table-Driven: Lo Standard di Go
I test table-driven riducono la duplicazione e rendono banale l'aggiunta di nuovi casi. Ogni riga nella tabella rappresenta uno scenario con input e output attesi. Questo pattern appare nella libreria standard di Go e nelle codebase di produzione in tutto il settore.
package validator
import "testing"
func TestValidateEmail(t *testing.T) {
tests := []struct {
name string
email string
wantErr bool
}{
{"valid email", "user@example.com", false},
{"missing at sign", "userexample.com", true},
{"empty string", "", true},
{"unicode local part", "user@例え.jp", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := ValidateEmail(tt.email)
if (err != nil) != tt.wantErr {
t.Errorf("ValidateEmail(%q) error = %v, wantErr %v",
tt.email, err, tt.wantErr)
}
})
}
}Il metodo t.Run crea subtest che vengono eseguiti in modo indipendente. I subtest falliti riportano quale caso specifico è fallito senza interrompere gli altri casi.
Dependency Injection tramite Interfacce
Le interfacce Go permettono di testare codice che dipende da sistemi esterni: database, client HTTP, file system. Definire un'interfaccia minimale nel punto di utilizzo consente di sostituire le implementazioni reali con test double.
package order
// Repository definisce i metodi di accesso ai dati necessari a questo servizio
type Repository interface {
FindByID(id string) (*Order, error)
Save(order *Order) error
}
// Service gestisce la logica di business degli ordini
type Service struct {
repo Repository
}
// NewService crea un servizio con il repository fornito
func NewService(repo Repository) *Service {
return &Service{repo: repo}
}
// Process valida e salva un ordine
func (s *Service) Process(order *Order) error {
if order.Total <= 0 {
return ErrInvalidTotal
}
return s.repo.Save(order)
}Il Service accetta qualsiasi tipo che soddisfi Repository. Il codice di produzione passa un'implementazione supportata da database. I test passano un mock o uno stub.
Mocking con gomock e mockgen
Il pacchetto gomock genera implementazioni mock dalle definizioni delle interfacce. Il comando mockgen legge i file sorgente e produce struct mock che registrano le chiamate e restituiscono valori configurati.
# Genera mock per l'interfaccia Repository
mockgen -source=service.go -destination=mocks/repository_mock.go -package=mocksUtilizzo del mock generato nei test:
package order
import (
"testing"
"github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
"yourmodule/order/mocks"
)
func TestService_Process_SavesValidOrder(t *testing.T) {
ctrl := gomock.NewController(t)
mockRepo := mocks.NewMockRepository(ctrl)
order := &Order{ID: "123", Total: 99.99}
// Aspettarsi che Save venga chiamato una volta con questo ordine, restituire nil
mockRepo.EXPECT().
Save(order).
Return(nil).
Times(1)
svc := NewService(mockRepo)
err := svc.Process(order)
assert.NoError(t, err)
}
func TestService_Process_RejectsZeroTotal(t *testing.T) {
ctrl := gomock.NewController(t)
mockRepo := mocks.NewMockRepository(ctrl)
// Nessuna chiamata attesa al repo poiché la validazione fallisce prima
svc := NewService(mockRepo)
err := svc.Process(&Order{ID: "456", Total: 0})
assert.ErrorIs(t, err, ErrInvalidTotal)
}Il mock verifica che Save sia stato chiamato esattamente una volta con l'argomento atteso. Se il codice sotto test non chiama mai Save, o lo chiama con argomenti diversi, il test fallisce.
Utilizzo di testify per Assertion e Suite
testify fornisce funzioni di assertion che producono messaggi di errore più chiari rispetto ai controlli if manuali. Il pacchetto assert continua l'esecuzione dopo i fallimenti. Il pacchetto require interrompe il test immediatamente.
package user
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestParseUser(t *testing.T) {
input := `{"id": "u1", "name": "Alice"}`
user, err := ParseUser(input)
require.NoError(t, err, "parsing should not fail")
assert.Equal(t, "u1", user.ID)
assert.Equal(t, "Alice", user.Name)
assert.Empty(t, user.Email, "email should default to empty")
}La chiamata require.NoError interrompe il test se il parsing fallisce, prevenendo panic da puntatori nil nelle assertion successive.
Pronto a superare i tuoi colloqui su Go?
Pratica con i nostri simulatori interattivi, flashcards e test tecnici.
Testare Handler HTTP con httptest
Il pacchetto net/http/httptest fornisce ResponseRecorder per catturare l'output dell'handler e Server per eseguire un server di test locale. La maggior parte degli unit test utilizza ResponseRecorder per evitare l'overhead di rete.
package api
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
func TestHealthHandler(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/health", nil)
rec := httptest.NewRecorder()
HealthHandler(rec, req)
assert.Equal(t, http.StatusOK, rec.Code)
assert.JSONEq(t, `{"status": "ok"}`, rec.Body.String())
}Per handler che chiamano servizi esterni, si inietta un client mock o si utilizza httptest.NewServer per creare un backend fittizio.
Domande Frequenti nei Colloqui sul Testing in Go
Gli intervistatori valutano la conoscenza del testing attraverso domande che rivelano l'esperienza con codebase reali. Ecco i pattern che distinguono i candidati preparati:
"Come testeresti una funzione che chiama un'API esterna?"
Definire un'interfaccia per il client HTTP, iniettarla nella funzione o struct, e fornire un mock che restituisce risposte predeterminate. Questo evita le chiamate di rete e rende i test deterministici.
"Quando useresti uno stub rispetto a un mock?"
Gli stub restituiscono risposte predefinite senza verificare come sono stati chiamati. I mock verificano che metodi specifici siano stati chiamati con argomenti specifici. Si usano gli stub quando il valore di ritorno conta più dell'interazione. Si usano i mock quando verificare l'interazione è lo scopo del test.
"Cosa sono i test table-driven e perché Go li preferisce?"
I test table-driven definiscono i casi di test come dati in uno slice di struct. Riducono il boilerplate, rendono banale l'aggiunta di casi e producono output di test leggibili con t.Run. Il pattern si allinea con la preferenza di Go per codice esplicito e ripetitivo rispetto ad astrazioni elaborate.
"Come gestisci le fixture di test o il setup condiviso tra più test?"
Utilizzare TestMain per setup e teardown a livello di package. Utilizzare funzioni helper o test suite (da testify) per setup condiviso tra test correlati. Evitare lo stato globale che crea accoppiamento tra i test.
Preparazione per un colloquio Go? Il modulo Go Testing copre questi pattern in profondità con domande di pratica.
Eseguire Test con Coverage e Race Detection
Il comando go test accetta flag che rivelano percorsi di codice non testati e bug di concorrenza.
# Eseguire test con report di coverage
go test -cover ./...
# Generare visualizzazione HTML della coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html
# Rilevare race condition (più lento, ma trova bug reali)
go test -race ./...Il race detector instrumenta gli accessi alla memoria e segnala letture e scritture concorrenti sulla stessa variabile. Eseguire i test con -race nella CI cattura bug che si manifestano solo con timing specifici.
Organizzare i Test in Codebase di Grandi Dimensioni
Man mano che i progetti crescono, l'organizzazione dei test influisce sulla manutenibilità:
- Test same-package (
package foo) hanno accesso a funzioni e campi non esportati. Da usare per unit test che verificano il comportamento interno. - Test external-package (
package foo_test) importano il package come codice esterno. Da usare per test di integrazione e per verificare l'API pubblica. - Directory testdata memorizzano fixture come file JSON, output golden o database seed. Go le ignora durante il build.
- Build tag separano gli unit test dai test di integrazione che richiedono sistemi esterni. Eseguire
go test -tags=integration ./...per includerli.
Per approfondire le best practice di Go oltre il testing, l'articolo sui pattern di concorrenza tratta goroutine e channel.
Cosa Ricordare sul Testing in Go
- Il pacchetto
testingè distribuito con Go e non richiede dipendenze esterne - I test table-driven riducono la duplicazione e sono il pattern atteso nei colloqui
- Le interfacce abilitano la dependency injection; definirle nel punto di utilizzo, non nell'implementazione
gomockgenera mock dalle interfacce;testifyfornisce assertion più pulitehttptest.ResponseRecordercattura l'output degli handler senza overhead di rete- Il flag
-racerileva bug di concorrenza che gli unit test altrimenti mancano - Le metriche di coverage guidano l'attenzione ma non garantiscono la correttezza
Inizia a praticare!
Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.
Sapresti trovare il bug in Go?
Uno snippet reale, un bug nascosto, un tentativo al giorno. Senza account per provare.

Scritto da
Anthony Fillion-MailletFondatore di SharpSkill
Sviluppatore fullstack da oltre 10 anni. Guida SharpSkill e risponde di tutto ciò che vi viene pubblicato.
Aggiornato il 24 agosto 2026
Tag
Condividi
Articoli correlati

Gestione degli Errori in Go nel 2026: Pattern, Wrapping e Domande per Colloqui Tecnici
Guida completa alla gestione degli errori in Go: pattern moderni, error wrapping, errors.Is/As e domande frequenti nei colloqui tecnici per sviluppatori.

Go Design Patterns: Pattern essenziali e domande da colloquio per sviluppatori Go
I sei design pattern Go piu importanti con codice pronto per la produzione: Functional Options, Strategy, Factory, Observer, Middleware e Struct Embedding. Con domande frequenti nei colloqui tecnici.

Interfacce Go Avanzate nel 2026: Composizione, Type Assertion e Domande da Colloquio
Una guida completa alle interfacce Go: composizione tramite embedding, type assertion sicure, generics autoreferenziali in Go 1.26 e le domande più frequenti nei colloqui tecnici per sviluppatori senior.