Testes em Sistemas Distribuídos

Testes unitários por serviço

Tutorial real de testes unitários para Java, Python e Go no monorepo.

Avançado 40 min 30 pontos Leitura 0%

Nesta aula você vai

  • Escrever testes unitários focados em regra de negócio
  • Isolar dependências externas com mocks consistentes
  • Executar suíte unitária por serviço com comandos reproduzíveis

Testes unitários por serviço

Nesta aula você vai montar uma base de testes unitários para os serviços customer-service, order-service e payment-service, com foco em comportamento de domínio.

Arquivos usados

  • services/customer-service/src/main/java/com/amo/customer/service/CustomerService.java
  • services/customer-service/src/test/java/com/amo/customer/service/CustomerServiceTest.java
  • services/order-service/app/domain/order_service.py
  • services/order-service/tests/unit/test_order_service.py
  • services/payment-service/internal/service/payment_service.go
  • services/payment-service/internal/tests/unit/payment_service_test.go

Passo 1 - Defina o contrato de comportamento

Antes de testar framework, descreva cenários de domínio:

  1. Cliente com e-mail duplicado deve falhar.
  2. Pedido com item vazio deve falhar.
  3. Pagamento de pedido cancelado não pode ser autorizado.

Exemplo de regra no Java:

public Customer create(CustomerInput input) {
  if (repository.existsByEmail(input.email())) {
    throw new BusinessException("EMAIL_ALREADY_EXISTS");
  }
  return repository.save(Customer.from(input));
}

Passo 2 - Implemente teste unitário no Customer (Java)

Crie CustomerServiceTest.java:

@ExtendWith(MockitoExtension.class)
class CustomerServiceTest {
  @Mock CustomerRepository repository;
  @InjectMocks CustomerService service;

  @Test
  void shouldFailWhenEmailAlreadyExists() {
    when(repository.existsByEmail("ana@acme.com")).thenReturn(true);
    assertThrows(BusinessException.class,
      () -> service.create(new CustomerInput("Ana", "ana@acme.com")));
  }
}

Comando:

cd services/customer-service
./mvnw -q test -Dtest=CustomerServiceTest

Passo 3 - Implemente teste unitário no Order (Python)

Crie tests/unit/test_order_service.py:

def test_create_order_fails_without_items(order_service):
    with pytest.raises(DomainError, match="ORDER_ITEMS_REQUIRED"):
        order_service.create_order(customer_id="c-1", items=[])

Comando:

cd services/order-service
pytest tests/unit -q

Passo 4 - Implemente teste unitário no Payment (Go)

Crie internal/tests/unit/payment_service_test.go:

func TestAuthorize_ShouldFailForCancelledOrder(t *testing.T) {
  repo := mocks.NewPaymentRepository(t)
  svc := service.NewPaymentService(repo)
  _, err := svc.Authorize(service.AuthorizeInput{OrderStatus: "CANCELLED"})
  require.Error(t, err)
  require.Contains(t, err.Error(), "ORDER_CANCELLED")
}

Comando:

cd services/payment-service
go test ./internal/tests/unit/...

Passo 5 - Rode tudo no monorepo

make test-unit

Se o projeto ainda não tiver alvo no Makefile, adicione:

test-unit:
	cd services/customer-service && ./mvnw -q test
	cd services/order-service && pytest tests/unit -q
	cd services/payment-service && go test ./internal/tests/unit/...

Checklist de validação

  • Testes não dependem de banco, Kafka ou Redis.
  • Nomes dos testes descrevem regra de negócio.
  • Falhas exibem código de erro de domínio (EMAIL_ALREADY_EXISTS, ORDER_ITEMS_REQUIRED).
  • Execução total em menos de 30 segundos localmente.

Resumo

Você criou uma suíte unitária prática para três linguagens, com comandos reproduzíveis e foco em regra de negócio, preparando terreno para integração e contrato nas próximas aulas.