Mocks e Fixtures em Rust: como testar código sem dor

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: Por que testes em Rust não são chatos



Se você já escreveu testes para código em Python ou JavaScript, certamente já se deparou com a situação: seu método chama uma API externa, lê um arquivo do disco ou acessa um banco de dados. Executar esses testes em um ambiente real é uma dor de cabeça. Eles são lentos, não confiáveis (e se a API cair?) e exigem configuração de ambiente.



Em Rust, a situação não é diferente. Só que Rust é uma linguagem de sistema, e aqui os testes devem ser rápidos, confiáveis e determinísticos. É exatamente para isso que servem os objetos mock e as fixtures.



Neste artigo, vamos abordar como escrever testes em Rust que não dependem do mundo externo. Você aprenderá a substituir dependências, preparar dados para testes e fazer isso de forma elegante. Vamos lá!



O que são objetos mock e fixtures?



Objetos Mock (mocks)

Um objeto mock é uma imitação de um componente real do sistema. Você cria um objeto que se comporta conforme necessário para o teste: retorna valores definidos, lança erros ou verifica se seus métodos foram chamados com os argumentos corretos.



Imagine que você tem um serviço de envio de e-mail. No teste, você não quer realmente enviar e-mails — você usa um mock que apenas verifica se o serviço foi chamado com o endereço e texto corretos.



Fixtures

Fixtures são dados preparados ou estados necessários para executar o teste. Por exemplo, um banco de dados de teste, um arquivo de configuração, estruturas pré-definidas. Em Rust, fixtures são frequentemente implementadas por meio de funções comuns que retornam objetos prontos.



Juntos, mocks e fixtures fornecem controle total sobre o ambiente do teste. Você testa apenas sua lógica, e não o funcionamento de serviços externos.



Princípios básicos de teste em Rust



Antes de mergulharmos nos mocks, vamos relembrar como os testes funcionam em Rust. Um teste unitário comum se parece com isso:



#[cfg(test)]mod tests {    use super::*;

#[test] fn test_addition() { assert_eq!(2 + 2, 4); }}


Tudo simples. Mas quando seu código começa a depender de recursos externos (arquivos, requisições HTTP, bancos de dados), surgem problemas. A solução é inverter as dependências e usar o padrão Dependency Injection (Injeção de Dependência).



Exemplo prático: serviço de carregamento de dados



Suponha que temos uma estrutura DataLoader, que carrega dados de uma API externa e os processa. Queremos testar o processamento sem chamar a API real.



Primeiro, definimos uma trait (interface) para a fonte de dados:



pub trait DataSource {    fn fetch_data(&self) -> Result<String, String>;}


Agora, implementamos a fonte real, que faz uma requisição HTTP (de forma simplificada):



pub struct RealDataSource {    pub url: String,}

impl DataSource for RealDataSource { fn fetch_data(&self) -> Result<String, String> { // Aqui haveria uma requisição HTTP real Ok(format!("Dados de {}", self.url)) }}


E o serviço que usa essa fonte:



pub struct DataLoader<T: DataSource> {    source: T,}

impl<T: DataSource> DataLoader<T> { pub fn new(source: T) -> Self { DataLoader { source } }

pub fn load_and_process(&self) -> Result<String, String> { let raw_data = self.source.fetch_data()?; // Algum processamento Ok(format!("Processado: {}", raw_data)) }}


Observe: DataLoader não depende de uma implementação específica de DataSource. Ele funciona com qualquer tipo que implemente essa trait. Esta é a chave para a testabilidade.



Criando um objeto mock manualmente



A maneira mais simples é criar uma estrutura de teste

Blogs

Book Recommendations