Arquitetura Limpa em Go – Parte 5 – Testes

Aborde a importância dos testes na Arquitetura Limpa em Go, com dicas práticas e exemplos que elevam a qualidade e a confiabilidade do seu código.

Neste post vamos falar sobre esse assunto polêmico entre os devs: A importância dos testes no contexto da Arquitetura Limpa. Dominar e adquirir o hábito de fazer testes é  garantir a qualidade e a confiabilidade do nosso código. E o melhor de tudo é que em Go você não precisa de vários frameworks diferentes pra criar testes. Frameworks existem, é claro, mas a biblioteca padrão já tem tudo que você precisa.

Por Que Testar em uma Arquitetura Limpa?

Em uma Arquitetura Limpa, cada componente tem seu lugar e propósito. Testes, aqui, são essenciais para garantir que cada peça do nosso software não apenas funciona isoladamente, mas também em harmonia com o restante do sistema. Eles são a nossa rede de segurança, garantindo que mudanças e melhorias não quebrem funcionalidades existentes.

Testes Unitários

Comecemos com testes unitários. Os testes unitários são a espinha dorsal da nossa confiança no código, eles garantem que cada função ou método funcione perfeitamente em isolamento. Testes unitários testam a menor parte do código, como funções ou métodos, de forma isolada. Em Go, isso é bastante direto, graças à sua sintaxe clara e ferramentas de teste integradas.

Imagine que temos um serviço que valida usuários:

package users

type User struct {
    ID    int
    Name  string
    Email string
}

type UserService struct {
    // Dependências
}

func (s *UserService) IsValid(user User) bool {
    // Validação simples para exemplo
    return user.Name != "" && user.Email != ""
}

O teste unitário para IsValid seria:

package users

import "testing"

func TestUserService_IsValid(t *testing.T) {
    userService := UserService{}

    testCases := []struct {
        name     string
        user     User
        expected bool
    }{
        {"valid user", User{1, "John Doe", "[email protected]"}, true},
        {"invalid user", User{2, "", ""}, false},
    }

    for _, tc := range testCases {
        t.Run(tc.name, func(t *testing.T) {
            if got := userService.IsValid(tc.user); got != tc.expected {
                t.Errorf("IsValid() = %v; want %v", got, tc.expected)
            }
        })
    }
}

Testes de Integração: Conectando as Peças

Os testes de integração verificam como diferentes módulos do sistema interagem. Eles são essenciais para garantir que a integração entre as camadas da Arquitetura Limpa funcione como esperado.

Vamos considerar um exemplo onde temos um repositório de usuários:

package users

type UserRepository struct {
    // Implementação de conexão com o banco
}

func (r *UserRepository) GetByID(id int) (*User, error) {
    // Implementação para buscar um usuário
}

Um teste de integração para GetByID poderia ser:

package users

import (
    "testing"
    "database/sql"
)

func TestUserRepository_GetByID(t *testing.T) {
    db, err := sql.Open("postgres", "your-database-connection-string")
    if err != nil {
        t.Fatalf("could not open db connection: %v", err)
    }

    repo := UserRepository{db}
    user, err := repo.GetByID(1)

    if err != nil {
        t.Fatalf("error getting user: %v", err)
    }

    if user == nil {
        t.Fatalf("expected user, got nil")
    }
}

Testes de Ponta a Ponta

Os testes de ponta a ponta são essenciais para verificar o comportamento do sistema como um todo, simulando o ambiente de produção.

Para um serviço web em Go, um teste de ponta a ponta envolveria simular requisições HTTP completas.

package main

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

func TestHTTPServer_E2E(t *testing.T) {
    server := httptest.NewServer(setupServer()) // setupServer configura as rotas
    defer server.Close()

    resp, err := http.Get(server.URL + "/users/1")
    if err != nil {
        t.Fatalf("could not send GET request: %v", err)
    }

    if resp.StatusCode != http.StatusOK {
        t.Fatalf("expected status OK; got %v", resp.Status)
    }

    // Mais verificações podem ser adicionadas aqui
}

Conclusão

Implementar testes abrangentes em uma Arquitetura Limpa não é apenas uma prática recomendada; É um hábito que deve ser desenvolvido por todos nós programadores. Eles trazem clareza e confiança ao nosso código. Com testes bem estruturados, elevamos a qualidade do nosso código, garantindo que cada elemento da nossa aplicação funcione harmoniosamente, mesmo conforme ela evolui e cresce.

No próximo post falaremos sobre Injeção de Dependência em Go, e como ela se relaciona com a Arquitetura Limpa.

Let’s code!

1 comment
Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Post Anterior

Arquitetura Limpa em Go – Parte 4 – Padrões de Projeto

Próximo Post

Arquitetura Limpa em Go – Parte 6 – Injeção de Dependência

Posts Relacionados