---
title: "Mocks em Go: Testify, gomock, Fakes e httptest na Prática"
url: "https://golang.com.br/blog/mocks-go-testify-gomock-fakes-httptest/"
markdown_url: "https://golang.com.br/blog/mocks-go-testify-gomock-fakes-httptest.MD"
description: "Mocks em Go na prática: test doubles, httptest, fakes à mão, testify/mock e gomock. Aprenda quando usar cada um e testar serviços sem depender de banco ou API externa."
date: "2026-07-11"
author: "Golang Brasil"
---

# Mocks em Go: Testify, gomock, Fakes e httptest na Prática

Mocks em Go na prática: test doubles, httptest, fakes à mão, testify/mock e gomock. Aprenda quando usar cada um e testar serviços sem depender de banco ou API externa.


A pergunta mais comum de quem escreve o primeiro teste de serviço em Go não é "como faço `assert`". É "como testo isso sem chamar o banco de verdade ou a API externa?". É a pergunta certa: testes que dependem de PostgreSQL, Redis e SDK de nuvem são lentos, instáveis e falham por motivos que não têm a ver com a sua lógica. A resposta é a mesma que sustenta o resto do código idiomático: **dependa de interfaces pequenas e troque a implementação no teste**. Este guia mostra como fazer isso com `httptest`, fakes à mão, `testify/mock` e `gomock`, e quando cada um vale a pena em 2026.

Antes de continuar, vale uma verdade que economiza muito tempo: em Go, o "mock" muitas vezes é só um fake simples que você escreve em 15 linhas. Bibliotecas de mock existem e ajudam, mas a primeira opção deveria ser uma implementação em memória de uma interface. Se você ainda não está confortável com interfaces por trás das dependências, leia o guia de [interfaces em Go](/aprenda/golang-interfaces/) e o de [dependency injection sem framework](/blog/dependency-injection-go-sem-framework/) — sem isso, mockar vira sofrimento.

## Vocabulário: fake, stub, spy e mock

A comunidade de testes usa essas palavras com sentidos diferentes de fórum para fórum. Esta é a convenção que ajuda a decidir:

| Test double | O que faz | Quando usar |
|---|---|---|
| **Stub** | Devolve respostas fixas, ignora como foi chamado | Isolar uma entrada simples |
| **Fake** | Implementação real, porém simplificada (ex.: repositório em memória) | Testar lógica de domínio de ponta a ponta |
| **Spy** | Registra chamadas para você inspecionar depois | Verificar que algo *foi* chamado, sem bloquear |
| **Mock** | Vem com expectativas; falha se o comportamento não ocorrer | Garantir interações exatas com o colaborador |

A regra prática em Go: **fake primeiro, mock depois**. Fakes tornam o teste legível e resistente a refatoração; mocks deixam o teste frágil quando a implementação muda. Combine com [testes de tabela](/blog/testes-tabela-go-guia-table-driven-tests/) para cobrir vários cenários com pouco código.

## O pré-requisito: depender de interfaces

Para trocar uma dependência no teste, o consumidor precisa depender de uma interface, não de um tipo concreto. Um serviço que grava usuários não deveria saber que existe PostgreSQL:

```go
package main

import (
	"context"
	"errors"
)

var ErrNaoEncontrado = errors.New("usuário não encontrado")

type Usuario struct {
	ID   int
	Nome string
}

type RepositorioUsuario interface {
	Salvar(ctx context.Context, u Usuario) error
	Buscar(ctx context.Context, id int) (Usuario, error)
}

type Servico struct {
	repo RepositorioUsuario
	prox int
}

func NovoServico(repo RepositorioUsuario) *Servico {
	return &Servico{repo: repo}
}

func (s *Servico) Criar(ctx context.Context, nome string) (Usuario, error) {
	u := Usuario{ID: s.prox + 1, Nome: nome}
	if err := s.repo.Salvar(ctx, u); err != nil {
		return Usuario{}, err
	}
	s.prox = u.ID
	return u, nil
}
```

A partir daqui, qualquer coisa que implemente `Salvar` e `Buscar` serve como dependência — inclusive um fake. Esse é o mesmo princípio que sustenta [Clean Architecture em Go](/blog/clean-architecture-go-sem-overengineering/): a regra de negócio não conhece os detalhes de infraestrutura.

## Fake à mão: o caminho idiomático

O fake mais útil é um repositório em memória. Ele é rápido, não exige biblioteca e se comporta como o de verdade o suficiente para testar a lógica:

```go
package main

import (
	"context"
	"testing"
)

type MemoriaUsuario struct {
	dados map[int]Usuario
}

func NewMemoriaUsuario() *MemoriaUsuario {
	return &MemoriaUsuario{dados: make(map[int]Usuario)}
}

func (m *MemoriaUsuario) Salvar(_ context.Context, u Usuario) error {
	m.dados[u.ID] = u
	return nil
}

func (m *MemoriaUsuario) Buscar(_ context.Context, id int) (Usuario, error) {
	u, ok := m.dados[id]
	if !ok {
		return Usuario{}, ErrNaoEncontrado
	}
	return u, nil
}

func TestServico_Criar_PersisteEGeraID(t *testing.T) {
	repo := NewMemoriaUsuario()
	svc := NovoServico(repo)

	criado, err := svc.Criar(context.Background(), "Diego")
	if err != nil {
		t.Fatalf("erro inesperado: %v", err)
	}
	buscado, err := repo.Buscar(context.Background(), criado.ID)
	if err != nil {
		t.Fatalf("não encontrou o que salvou: %v", err)
	}
	if buscado.Nome != "Diego" {
		t.Errorf("esperado Diego, veio %s", buscado.Nome)
	}
}
```

Esse teste roda em microssegundos, não precisa de container e quebra nos momentos certos: quando a lógica de `Criar` muda de comportamento. Para dependências externas pesadas (PostgreSQL, Redis), guarde o [Testcontainers](/blog/go-testcontainers-testes-integracao-containers/) para um conjunto menor de testes de integração — não para cada teste unitário.

## httptest: handlers e clientes HTTP sem servidor

Para testar um handler HTTP, a biblioteca padrão oferece `net/http/httptest`. `httptest.NewRequest` monta a requisição e `httptest.NewRecorder` captura a resposta, sem subir servidor:

```go
package main

import (
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

func criarUsuario(repo RepositorioUsuario) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		_ = repo
		if r.Body == nil {
			http.Error(w, "corpo vazio", http.StatusBadRequest)
			return
		}
		w.WriteHeader(http.StatusCreated)
	}
}

func TestCriarUsuario_400_QuandoJSONInvalido(t *testing.T) {
	req := httptest.NewRequest(http.MethodPost, "/usuarios", strings.NewReader(`{inválido`))
	rec := httptest.NewRecorder()

	criarUsuario(NewMemoriaUsuario()).ServeHTTP(rec, req)

	if rec.Code != http.StatusBadRequest && rec.Code != http.StatusCreated {
		// handler ilustra o uso de httptest; ajuste a asserção à sua rota real
		t.Fatalf("status inesperado: %d", rec.Code)
	}
}
```

Para testar um **cliente** HTTP (o seu código que chama uma API externa), use `httptest.NewServer` para subir um servidor falso que devolve respostas controladas — assim você testa timeout, retry e parsing sem depender da internet. Lembre de configurar `context` com prazo nos testes que envolvem rede; o guia de [context, timeout e cancelamento](/blog/context-timeout-cancelamento-go/) cobre o padrão.

## testify/mock: asserções e expectativas leves

Quando você precisa garantir *interações* (a dependência foi chamada com quais argumentos, quantas vezes), `testify/mock` é a opção mais difundida no Brasil. Você embute `mock.Mock` na struct e chama `Called`:

```go
package main

import (
	"context"
	"errors"
	"testing"

	"github.com/stretchr/testify/mock"
)

type MockRepositorioUsuario struct {
	mock.Mock
}

func (m *MockRepositorioUsuario) Salvar(ctx context.Context, u Usuario) error {
	args := m.Called(ctx, u)
	return args.Error(0)
}

func TestServico_Criar_PropagaErroDoRepositorio(t *testing.T) {
	repo := new(MockRepositorioUsuario)
	repo.On("Salvar", mock.Anything, mock.Anything).
		Return(errors.New("falha de conexão"))
	svc := NovoServico(repo)

	_, err := svc.Criar(context.Background(), "Diego")

	if err == nil {
		t.Fatal("esperado erro do repositório")
	}
	repo.AssertExpectations(t)
}
```

O ponto forte é a verbosidade baixa e o fato de o mock ser verificado em runtime, sem passo de geração de código. O ponto fraco também existe: como o nome do método é uma string (`"Salvar"`), o teste só falha quando roda, e um método renomeado não quebra a compilação.

## gomock: mocks gerados e tipados

Em bases grandes, `gomock` (com `mockgen`) gera o mock a partir da interface e devolve chamadas tipadas. O teste falha na compilação quando a interface muda, o que escala melhor em times grandes:

```go
package main

import (
	"context"
	"testing"

	"go.uber.org/mock/gomock"
)

//go:generate go run go.uber.org/mock/mockgen -source=servico.go -destination=mock_usuario_test.go -package=main

func TestServico_Criar_ComGomock(t *testing.T) {
	ctrl := gomock.NewController(t)

	repo := NewMockRepositorioUsuario(ctrl)
	repo.EXPECT().Salvar(gomock.Any(), Usuario{Nome: "Diego"}).Return(nil)
	svc := NovoServico(repo)

	if _, err := svc.Criar(context.Background(), "Diego"); err != nil {
		t.Fatalf("erro inesperado: %v", err)
	}
}
```

O custo é o passo de geração (`go generate`) e uma curva um pouco maior. Para projetos pequenos e médios, `testify` costuma pagar menos; em monorepos com dezenas de serviços, `gomock` evita mocks desatualizados.

## Qual escolher

Decida nesta ordem:

1. **Lógica de domínio:** fake à mão. É legível, rápido e não acopla o teste a detalhes de implementação.
2. **Handlers e clientes HTTP:** `httptest`, da biblioteca padrão.
3. **Poucas interações a verificar:** `testify/mock` — leve e sem geração de código.
4. **Muitas interfaces, time grande, refatorações frequentes:** `gomock` — paga o investimento em segurança de tipos.

Em qualquer caso, **não mocke o que não é seu**. Bancos, SDKs de nuvem e bibliotecas de terceiros mudam; abstraia-os atrás de uma interface própria e troque a implementação no teste. É exatamente o que o guia de [API REST com Go](/aprenda/api-rest-go/) recomenda para manter os handlers testáveis.

## Armadilhas comuns

- **Mockar tipos concretos:** se o consumidor depende de um `*sql.DB` direto, não dá para trocar. Sempre depender de interface.
- **Excesso de `EXPECT`:** quando o teste lista cada chamada, qualquer refatoração inofensiva o quebra. Prefira fakes e reserve as expectativas estritas ao que realmente importa.
- **Testar o mock em vez do código:** se o teste só verifica que você chamou o que você mandou chamar, ele não testa comportamento. Use fakes para verificar o *resultado*.
- **Esquecer o `context` e o timeout:** testes de cliente HTTP sem prazo ficam pendurados no CI. Sempre passe um `context` com `WithTimeout`.
- **Ignorar caminhos de erro:** table-driven tests com fakes cobrem felicidade e erro de graça — não teste só o caso que funciona.

## Testes e carreira

Saber isolar dependências com test doubles é uma das habilidades mais cobradas em [entrevistas técnicas de Go](/carreira/entrevista-tecnica-go-2026/) e aparece nas [perguntas comuns de entrevista](/aprenda/perguntas-entrevista-go/). Quem chega e mostra um serviço com interface pequena, fake em memória e tabela de testes passa uma imagem de senioridade que "só rodar no Postman" não passa. Para fechar o ciclo de qualidade, combine mocks com [TDD e CI/CD](/tutoriais/go-tdd-ci-cd/) e com [fuzz testing](/blog/fuzzing-go-testes-nativos/) para entradas inesperadas. A mesma ideia de "depender de interfaces para ficar testável" aparece em outras stacks: o <a href="https://python.dev.br/aprenda/api-rest-python/" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'python.dev.br' })">guia de API REST em Python</a> mostra como o padrão de injeção de dependência do FastAPI cumpre o mesmo papel de tornar o código testável sem acoplar a banco e serviços externos.
