# Testcontainers: Ambientes de Teste Isolados com Docker

**Meta Description:** Aprenda Testcontainers: containers Docker para testes, databases, message brokers, como criar ambientes reprodutíveis.

---

## O Que São Testcontainers?

Testcontainers é uma biblioteca que facilita o uso de containers Docker para testes automatizados. Cada teste consegue seu próprio container isolado.

### Benefícios

- **Isolamento** - Cada teste tem seu ambiente
- **Reprodutibilidade** - Mesmo ambiente sempre
- **Realismo** - Testa contra serviços reais
- **Cleanup automático** - Containers são removidos

---

## Setup

```xml
<!-- Java/Maven -->
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>testcontainers</artifactId>
    <version>1.19.3</version>
</dependency>

<!-- Python -->
pip install testcontainers

<!-- Go -->
go get github.com/testcontainers/testcontainers-go

<!-- Node.js -->
npm install @testcontainers
```

---

## Exemplos

### PostgreSQL (Python)

```python
# test_database.py
import pytest
from testcontainers.postgres import PostgresContainer
import psycopg2

@pytest.fixture(scope="module")
def postgres():
    with PostgresContainer("postgres:15-alpine") as pg:
        yield pg

def test_database_connection(postgres):
    # Get connection string
    connection_url = postgres.get_connection_url()
    
    # Connect
    conn = psycopg2.connect(connection_url)
    cursor = conn.cursor()
    
    # Test
    cursor.execute("SELECT version();")
    version = cursor.fetchone()
    
    assert "PostgreSQL" in version[0]
    
    cursor.close()
    conn.close()

def test_create_table(postgres):
    conn = psycopg2.connect(postgres.get_connection_url())
    cursor = conn.cursor()
    
    cursor.execute("""
        CREATE TABLE users (
            id SERIAL PRIMARY KEY,
            name VARCHAR(100) NOT NULL,
            email VARCHAR(100) UNIQUE NOT NULL
        )
    """)
    conn.commit()
    
    # Insert test data
    cursor.execute("""
        INSERT INTO users (name, email) 
        VALUES ('João', 'joao@example.com')
        RETURNING id
    """)
    user_id = cursor.fetchone()[0]
    
    # Verify
    cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
    user = cursor.fetchone()
    
    assert user[1] == "João"
    
    cursor.close()
    conn.close()
```

### Redis (Python)

```python
# test_redis.py
import pytest
from testcontainers.redis import RedisContainer
import redis

@pytest.fixture
def redis_container():
    with RedisContainer("redis:7-alpine") as redis:
        yield redis

def test_redis_basic_operations(redis_container):
    client = redis.Redis.from_url(redis_container.get_connection_url())
    
    # Set and get
    client.set("key", "value")
    assert client.get("key") == b"value"
    
    # Expire
    client.setex("temp", 60, "temp_value")
    assert client.get("temp") == b"temp_value"
    
    # List
    client.lpush("mylist", "a", "b", "c")
    assert client.lrange("mylist", 0, -1) == [b'c', b'b', b'a']
    
    client.flushall()

def test_redis_pubsub(redis_container):
    client = redis.Redis.from_url(redis_container.get_connection_url())
    
    # Pub/Sub pattern
    pubsub = client.pubsub()
    pubsub.subscribe("test-channel")
    
    # Give time for subscription
    import time
    time.sleep(0.5)
    
    # Publish message
    client.publish("test-channel", "Hello, World!")
    
    # Receive message
    message = pubsub.get_message()
    assert message["type"] == "message"
    assert message["data"] == b"Hello, World!"
    
    pubsub.close()
```

### MongoDB (Java)

```java
// MongoDBTest.java
import org.testcontainers.containers.MongoDBContainer;
import org.junit.jupiter.api.*;

import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;

import static org.junit.jupiter.api.Assertions.*;

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class MongoDBTest {
    
    MongoDBContainer mongo = new MongoDBContainer("mongo:7");
    
    @BeforeAll
    void startContainer() {
        mongo.start();
    }
    
    @AfterAll
    void stopContainer() {
        mongo.stop();
    }
    
    @Test
    void testMongoConnection() {
        String connectionString = mongo.getReplicaSetUrl();
        
        try (var client = MongoClients.create(connectionString)) {
            MongoDatabase database = client.getDatabase("testdb");
            MongoCollection<org.bson.Document> collection = database.getCollection("users");
            
            // Insert document
            org.bson.Document user = new org.bson.Document()
                .append("name", "João Silva")
                .append("email", "joao@example.com");
            
            collection.insertOne(user);
            
            // Find document
            org.bson.Document found = collection.find().first();
            
            assertNotNull(found);
            assertEquals("João Silva", found.getString("name"));
        }
    }
}
```

### Docker Compose (Python)

```python
# test_integration.py
import pytest
from testcontainers.compose import DockerCompose

@pytest.fixture(scope="module")
def compose():
    with DockerCompose("/path/to/project") as compose:
        compose.start()
        yield compose
        compose.stop()

def test_all_services_running(compose):
    # Check if services are healthy
    exit_code, output = compose.exec("api", "curl -f http://localhost:8080/health")
    assert exit_code == 0
    
    exit_code, output = compose.exec("db", "pg_isready")
    assert exit_code == 0

def test_api_with_database(compose):
    # API should be able to connect to database
    exit_code, output = compose.exec(
        "api", 
        "python -c 'import psycopg2; print(\"OK\")'"
    )
    assert exit_code == 0
    assert "OK" in output.decode()
```

---

## Multiple Containers

```python
# test_full_stack.py
import pytest
from testcontainers.postgres import PostgresContainer
from testcontainers.redis import RedisContainer
from testcontainers.kafka import KafkaContainer

@pytest.fixture(scope="module")
def test_environment():
    # Start all containers
    postgres = PostgresContainer("postgres:15")
    redis = RedisContainer("redis:7")
    kafka = KafkaContainer()
    
    # Start them
    pg_url = postgres.start()
    redis_url = redis.start()
    kafka_bootstrap = kafka.start()
    
    yield {
        'postgres': pg_url,
        'redis': redis_url,
        'kafka': kafka_bootstrap
    }
    
    # Cleanup
    kafka.stop()
    redis.stop()
    postgres.stop()
```

---

## CI Integration

```yaml
# GitHub Actions
name: Tests with Testcontainers

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      docker:
        image: docker:24.0.5-cli
        options: --privileged
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      - name: Run tests
        run: |
          docker network create testnet || true
          docker compose -f docker-compose.test.yml up -d
          docker compose -f docker-compose.test.yml run test
```

---

## Conclusão

Testcontainers facilita testes com dependências reais. As chaves são:

1. **Isolamento** - Cada teste com seu ambiente
2. **Realismo** - Testa contra serviços reais
3. **Cleanup** - Containers são removidos após testes
4. **CI** - Funciona em pipelines
5. **Performance** - Mais lento que mocks, mais confiável
