# 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. - Published: 2026-08-24 - Updated: 2026-08-24 - Author: Anthony Fillion-Maillet - Tags: go, testing, unit-tests, mocking, interview - Reading time: 9 min --- 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. > **Cosa cercano gli intervistatori** > > 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. ```go // calculator_test.go 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](https://cs.opensource.google/go/go/+/refs/tags/go1.23.1:src/strings/strings_test.go) e nelle codebase di produzione in tutto il settore. ```go // validator_test.go 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. ```go // service.go 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](https://github.com/uber-go/mock) 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. ```bash # Genera mock per l'interfaccia Repository mockgen -source=service.go -destination=mocks/repository_mock.go -package=mocks ``` Utilizzo del mock generato nei test: ```go // service_test.go 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](https://github.com/stretchr/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. ```go // user_test.go 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. ## 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. ```go // handler_test.go 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](/technologies/go/interview-questions/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. ```bash # 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](/blog/go/go-concurrency-goroutines-channels) 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 - `gomock` genera mock dalle interfacce; `testify` fornisce assertion più pulite - `httptest.ResponseRecorder` cattura l'output degli handler senza overhead di rete - Il flag `-race` rileva bug di concorrenza che gli unit test altrimenti mancano - Le metriche di coverage guidano l'attenzione ma non garantiscono la correttezza --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/it/blog/go/go-testing-2026-unit-tests-mocks-interview