<p><strong>Meta Description:</strong> Aprenda a criar e usar fake services: mocking de APIs, databases, message queues. Como testar sem dependências externas.</p>

<hr>
<h2>Por Que Usar Fakes e Mocks?</h2>

<ul><li><strong>Isolamento</strong>: Testes não dependem de serviços externos</li><ol><li><strong>Velocidade</strong>: Mocks são mais rápidos que serviços reais</li>
<li><strong>Confiabilidade</strong>: Não há flaky tests por dependências</li>
<li><strong>Cobertura</strong>: Testar edge cases difíceis de reproduzir</li></ol></ul>

<hr>
<h2>Tipos de Fakes</h2>

<h3>1. Fake Objects</h3>

<p>Simulam comportamento de objetos reais.</p>

<pre><code># fakes/user_repository.py
class FakeUserRepository:
    def __init__(self):
        self.users = {}
        self._id_counter = 1

<p>def save(self, user):<br>        if not user.id:<br>            user.id = self._id_counter<br>            self._id_counter += 1<br>        self.users[user.id] = user<br>        return user</p>

<p>def find_by_id(self, id):<br>        return self.users.get(id)</p>

<p>def find_by_email(self, email):<br>        for user in self.users.values():<br>            if user.email == email:<br>                return user<br>        return None</p>

<p>def delete(self, id):<br>        if id in self.users:<br>            del self.users[id]<br>            return True<br>        return False</p>

<p># Uso em testes<br>def test_create_user():<br>    repo = FakeUserRepository()<br>    service = UserService(repo)</p>

<p>user = service.create_user("João", "joao@example.com")</p>

<p>assert user.id is not None<br>    assert user.name == "João"<br>    assert repo.find_by_email("joao@example.com") == user<br></code></pre></p>

<h3>2. Fake APIs</h3>

<p>Simulam endpoints HTTP.</p>

<pre><code># fakes/http_fake.py
import json
from http.server import HTTPServer, BaseHTTPRequestHandler
from threading import Thread
from typing import Dict, Callable

<p>class FakeAPIHandler(BaseHTTPRequestHandler):<br>    routes: Dict[str, Callable] = {}</p>

<p>def do_GET(self):<br>        self._handle_request('GET')</p>

<p>def do_POST(self):<br>        self._handle_request('POST')</p>

<p>def do_PUT(self):<br>        self._handle_request('PUT')</p>

<p>def do_DELETE(self):<br>        self._handle_request('DELETE')</p>

<p>def _handle_request(self, method):<br>        handler = self.routes.get((method, self.path))<br>        if handler:<br>            response = handler(self)<br>            self._send_response(response)<br>        else:<br>            self._send_response({'error': 'Not found'}, 404)</p>

<p>def _send_response(self, data, status=200):<br>        self.send_response(status)<br>        self.send_header('Content-Type', 'application/json')<br>        self.end_headers()<br>        self.wfile.write(json.dumps(data).encode())</p>

<p>def log_message(self, format, *args):<br>        pass  # Silenciar logs</p>

<p>class FakeAPIServer:<br>    def __init__(self, port=8080):<br>        self.port = port<br>        self.server = None<br>        self.thread = None<br>        self.routes = {}</p>

<p>def register(self, method, path, handler):<br>        self.routes[(method, path)] = handler<br>        FakeAPIHandler.routes = self.routes</p>

<p>def start(self):<br>        self.server = HTTPServer(('localhost', self.port), FakeAPIHandler)<br>        self.thread = Thread(target=self.server.serve_forever)<br>        self.thread.daemon = True<br>        self.thread.start()</p>

<p>def stop(self):<br>        if self.server:<br>            self.server.shutdown()</p>

<p># Uso<br>def payment_handler(request):<br>    return {<br>        'transaction_id': 'tx_123',<br>        'status': 'approved',<br>        'amount': 99.90<br>    }</p>

<p>fake_api = FakeAPIServer(8080)<br>fake_api.register('POST', '/api/payment', payment_handler)<br>fake_api.start()</p>

<p># Seus testes aqui</p>

<p>fake_api.stop()<br></code></pre></p>

<h3>3. Fake Databases (SQLite)</h3>

<pre><code># fakes/database.py
import sqlite3
import os

<p>class FakeDatabase:<br>    def __init__(self, in_memory=True):<br>        if in_memory:<br>            self.conn = sqlite3.connect(':memory:')<br>        else:<br>            self.db_path = 'test.db'<br>            self.conn = sqlite3.connect(self.db_path)</p>

<p>self.conn.row_factory = sqlite3.Row<br>        self._create_tables()</p>

<p>def _create_tables(self):<br>        cursor = self.conn.cursor()<br>        cursor.execute('''<br>            CREATE TABLE users (<br>                id INTEGER PRIMARY KEY AUTOINCREMENT,<br>                name TEXT NOT NULL,<br>                email TEXT UNIQUE NOT NULL<br>            )<br>        ''')<br>        cursor.execute('''<br>            CREATE TABLE products (<br>                id INTEGER PRIMARY KEY AUTOINCREMENT,<br>                name TEXT NOT NULL,<br>                price REAL NOT NULL<br>            )<br>        ''')<br>        self.conn.commit()</p>

<p>def execute(self, query, params=None):<br>        cursor = self.conn.cursor()<br>        if params:<br>            cursor.execute(query, params)<br>        else:<br>            cursor.execute(query)<br>        self.conn.commit()<br>        return cursor</p>

<p>def fetchone(self, query, params=None):<br>        cursor = self.execute(query, params)<br>        return cursor.fetchone()</p>

<p>def fetchall(self, query, params=None):<br>        cursor = self.execute(query, params)<br>        return cursor.fetchall()</p>

<p>def close(self):<br>        self.conn.close()<br>        if hasattr(self, 'db_path') and os.path.exists(self.db_path):<br>            os.remove(self.db_path)</p>

<p># Uso<br>def test_user_crud():<br>    db = FakeDatabase()</p>

<p># Create<br>    db.execute(<br>        'INSERT INTO users (name, email) VALUES (?, ?)',<br>        ('João', 'joao@example.com')<br>    )</p>

<p># Read<br>    user = db.fetchone('SELECT * FROM users WHERE email = ?', ('joao@example.com',))<br>    assert user['name'] == 'João'</p>

<p>db.close()<br></code></pre></p>

<hr>
<h2>Mocking Libraries</h2>

<h3>unittest.mock (Python)</h3>

<pre><code>from unittest.mock import Mock, patch, MagicMock

<p># Mock básico<br>def test_payment():<br>    mock_gateway = Mock()<br>    mock_gateway.charge.return_value = {'status': 'success'}</p>

<p>result = process_payment(100, mock_gateway)</p>

<p>mock_gateway.charge.assert_called_once_with(100)<br>    assert result == 'success'</p>

<p># Patch<br>@patch('myapp.services.payment_gateway')<br>def test_with_patch(mock_gateway):<br>    mock_gateway.charge.return_value = {'status': 'success'}</p>

<p>result = process_payment(100)</p>

<p>mock_gateway.charge.assert_called_once()</p>

<p># Spy (partial mock)<br>def test_spy():<br>    real_service = PaymentService()<br>    spy_service = Mock(wraps=real_service)</p>

<p>spy_service.process(100)</p>

<p>assert spy_service.process.called<br>    assert real_service.internal_method.called  # Real method foi chamado<br></code></pre></p>

<h3>WireMock (Java/Server)</h3>

<pre><code>// Java WireMock
import com.github.tomakehurst.wiremock.WireMockServer;
import static com.github.tomakehurst.wiremock.client.WireMock.*;

<p>WireMockServer wireMock = new WireMockServer(8080);<br>wireMock.start();</p>

<p>// Stub<br>wireMock.stubFor(post(urlEqualTo("/api/payment"))<br>    .withRequestBody(containing("amount"))<br>    .willReturn(aResponse()<br>        .withStatus(200)<br>        .withHeader("Content-Type", "application/json")<br>        .withBody("{"transaction_id":"tx_123","status":"approved"}")));</p>

<p>// Test<br>RestAssured.given()<br>    .contentType("application/json")<br>    .body("{"amount":100}")<br>.when()<br>    .post("http://localhost:8080/api/payment")<br>.then()<br>    .statusCode(200)<br>    .body("status", equalTo("approved"));</p>

<p>wireMock.stop();<br></code></pre></p>

<h3>MSW (Mock Service Worker)</h3>

<pre><code>// Mock API com MSW
import { setupWorker, rest } from 'msw';

<p>const handlers = [<br>  rest.post('/api/payment', (req, res, ctx) =&gt; {<br>    return res(<br>      ctx.status(200),<br>      ctx.json({<br>        transaction_id: 'tx_123',<br>        status: 'approved',<br>        amount: req.body.amount<br>      })<br>    );<br>  }),</p>

<p>rest.get('/api/users/:id', (req, res, ctx) =&gt; {<br>    return res(<br>      ctx.status(200),<br>      ctx.json({<br>        id: req.params.id,<br>        name: 'João Silva',<br>        email: 'joao@example.com'<br>      })<br>    );<br>  }),</p>

<p>rest.post('/api/users', (req, res, ctx) =&gt; {<br>    return res(<br>      ctx.status(201),<br>      ctx.json({<br>        id: 'new-user-id',<br>        ...req.body<br>      })<br>    );<br>  })<br>];</p>

<p>export const worker = setupWorker(...handlers);</p>

<p>// No test<br>import { worker } from './mocks/browser';</p>

<p>beforeAll(() =&gt; worker.start());<br>afterAll(() =&gt; worker.stop());</p>

<p>test('payment flow', async () =&gt; {<br>  render();</p>

<p>// MSW intercepta requisição<br>  await userEvent.type(screen.getByLabelText('Valor'), '100');<br>  await userEvent.click(screen.getByRole('button', { name: /pagar/i }));</p>

<p>expect(await screen.findByText(/transação aprovada/i)).toBeInTheDocument();<br>});<br></code></pre></p>

<hr>
<h2>TestContainers vs Fakes</h2>

<figure class="wp-block-table"><table>
<tr><th>Aspecto</th><th>TestContainers</th><th>Fakes</th></tr>
<tr><td><strong>Fidelidade</strong></td><td>Real service</td><td>Simulado</td></tr>
<tr><td><strong>Velocidade</strong></td><td>Lento (minutos)</td><td>Rápido (ms)</td></tr>
<tr><td><strong>Complexidade</strong></td><td>Alta</td><td>Baixa</td></tr>
<tr><td><strong>Bugs encontrados</strong></td><td>Mais realistas</td><td>Menos</td></tr>
<tr><td><strong>Manutenção</strong></td><td>Baixa</td><td>Alta (manter fakes)</td></tr>
</table></figure>

<h3>Quando Usar Cada</h3>

<pre><code># Use FAKE quando:
# - Precisa de respostas específicas/edge cases
# - Performance é crítica
# - Serviço externo é lento/não disponível
# - Quer testar comportamento, não implementação

<p># Use TestContainers quando:<br># - Precisa de comportamento real<br># - Testando integrações complexas<br># - Quer detectar problemas realistas<br># - Manutenção do fake seria muito complexa<br></code></pre></p>

<hr>
<h2>Conclusão</h2>

<p>Mocks e fakes são essenciais para testes isolados. As chaves são:<br><ol><li><strong>Isolar</strong> - Testes não devem depender de externos</li><br><li><strong>Velocidade</strong> - Fakes são rápidos</li><br><li><strong>Verossimilhança</strong> - Fakes devem ser realistas</li><br><li><strong>Manutenção</strong> - Mocks automáticos vs manuais</li><br><li><strong>Combinar</strong> - Fakes + TestContainers quando necessário</li></ol></p>