Rust

O que Todo Dev Deve Saber sobre Testes Unitários e de Integração em Rust com cargo test

8 min de leitura

O que Todo Dev Deve Saber sobre Testes Unitários e de Integração em Rust com cargo test

Fundamentos de Testes em Rust Testes em Rust são nativos à linguagem e totalmente integrados ao ecossistema via Cargo, o gerenciador de pacotes. Diferentemente de outras linguagens, você não precisa instalar frameworks externos para começar. A stdlib de Rust fornece tudo necessário através do atributo e da função e variações. O Cargo oferece dois tipos principais: testes unitários (escritos dentro dos módulos que testam) e testes de integração (em arquivos separados, testando a API pública). Executar testes é tão simples quanto . Rust compila testes como binários especiais e os executa em paralelo por padrão, tornando o feedback extremamente rápido mesmo em projetos maiores. Testes Unitários: Estrutura Básica Escrevendo seu primeiro teste unitário Testes unitários vivem no mesmo arquivo do código que testam. Use o atributo para organizar um módulo de testes que só compila quando você executa . Aqui está um exemplo real: A macro compara valores, valida booleanos, e verifica se o código falha corretamente. Use e para

<h2>Fundamentos de Testes em Rust</h2>

<p>Testes em Rust são nativos à linguagem e totalmente integrados ao ecossistema via <strong>Cargo</strong>, o gerenciador de pacotes. Diferentemente de outras linguagens, você não precisa instalar frameworks externos para começar. A stdlib de Rust fornece tudo necessário através do atributo <code>#[test]</code> e da função <code>assert!()</code> e variações.</p>

<p>O Cargo oferece dois tipos principais: <strong>testes unitários</strong> (escritos dentro dos módulos que testam) e <strong>testes de integração</strong> (em arquivos separados, testando a API pública). Executar testes é tão simples quanto <code>cargo test</code>. Rust compila testes como binários especiais e os executa em paralelo por padrão, tornando o feedback extremamente rápido mesmo em projetos maiores.</p>

<h2>Testes Unitários: Estrutura Básica</h2>

<h3>Escrevendo seu primeiro teste unitário</h3>

<p>Testes unitários vivem no mesmo arquivo do código que testam. Use o atributo <code>#[cfg(test)]</code> para organizar um módulo de testes que só compila quando você executa <code>cargo test</code>. Aqui está um exemplo real:</p>

<pre><code class="language-rust">// src/lib.rs

pub fn adicionar(a: i32, b: i32) -&gt; i32 {

a + b

}

pub fn dividir(a: i32, b: i32) -&gt; Result&lt;i32, String&gt; {

if b == 0 {

Err(&quot;Divisão por zero&quot;.to_string())

} else {

Ok(a / b)

}

}

#[cfg(test)]

mod testes {

use super::*;

#[test]

fn teste_adicionar_positivos() {

assert_eq!(adicionar(2, 3), 5);

}

#[test]

fn teste_adicionar_negativos() {

assert_eq!(adicionar(-1, -1), -2);

}

#[test]

fn teste_divisao_sucesso() {

assert_eq!(dividir(10, 2).unwrap(), 5);

}

#[test]

fn teste_divisao_por_zero() {

assert!(dividir(10, 0).is_err());

}

#[test]

#[should_panic(expected = &quot;Divisão por zero&quot;)]

fn teste_divisao_panic() {

if let Err(e) = dividir(10, 0) {

panic!(&quot;{}&quot;, e);

}

}

}</code></pre>

<p>A macro <code>assert_eq!()</code> compara valores, <code>assert!()</code> valida booleanos, e <code>#[should_panic]</code> verifica se o código falha corretamente. Use <code>unwrap()</code> e <code>is_err()</code> para trabalhar com <code>Result</code> em testes. Execute <code>cargo test</code> e você verá cada teste executado individualmente com feedback colorido.</p>

<h3>Truques úteis</h3>

<p>Use <code>#[ignore]</code> para pular testes durante desenvolvimento: <code>#[test] #[ignore]</code>. Execute apenas testes ignorados com <code>cargo test -- --ignored</code>. Para rodar um teste específico: <code>cargo test nome_do_teste</code>. Use <code>cargo test -- --nocapture</code> para ver saídas <code>println!</code> durante testes.</p>

<h2>Testes de Integração</h2>

<h3>Estrutura e organização</h3>

<p>Testes de integração testam sua biblioteca como um usuário externo faria. Eles ficam em um diretório especial <code>tests/</code> na raiz do projeto. Cada arquivo <code>.rs</code> dentro de <code>tests/</code> é compilado como um crate binário separado. Isso garante que você teste apenas a API pública.</p>

<p>Crie a estrutura: seu <code>Cargo.toml</code> fica em <code>/</code>, <code>src/lib.rs</code> em <code>/src</code>, e testes de integração em <code>/tests/*.rs</code>. Diferentemente de testes unitários, você não usa <code>#[cfg(test)]</code> nem módulos privados. Importe sua lib normalmente:</p>

<pre><code class="language-rust">// tests/integracao.rs

use seu_projeto::adicionar;

use seu_projeto::dividir;

#[test]

fn teste_fluxo_completo_adicionar() {

let resultado = adicionar(5, 5);

assert_eq!(resultado, 10);

}

#[test]

fn teste_fluxo_divisao_com_validacao() {

match dividir(20, 4) {

Ok(res) =&gt; assert_eq!(res, 5),

Err(_) =&gt; panic!(&quot;Divisão falhou inesperadamente&quot;),

}

}

#[test]

fn teste_integracao_multiplas_operacoes() {

let soma = adicionar(10, 20);

let resultado = dividir(soma, 5).expect(&quot;Divisão falhou&quot;);

assert_eq!(resultado, 6);

}</code></pre>

<p>Execute <code>cargo test --test integracao</code> para rodar apenas esse arquivo de testes. Se você tiver múltiplos arquivos em <code>tests/</code>, cada um é executado independentemente. Isso é poderoso para testar cenários complexos que envolvem múltiplas funções públicas trabalhando juntas.</p>

<h2>Cargo test: Controle Total</h2>

<h3>Executando e filtrando testes</h3>

<p>O comando <code>cargo test</code> é extremamente flexível. Execute <code>cargo test -- --help</code> para ver todas as opções. Alguns comandos práticos:</p>

<ul>

<li><code>cargo test</code> — executa todos os testes (unitários e integração)</li>

<li><code>cargo test --lib</code> — apenas testes unitários</li>

<li><code>cargo test --test *</code> — apenas testes de integração</li>

<li><code>cargo test adicionar</code> — filtra por nome (roda <code>teste_adicionar_positivos</code>, etc.)</li>

<li><code>cargo test -- --test-threads=1</code> — executa serialmente (útil para debugging)</li>

<li><code>cargo test -- --nocapture</code> — mostra <code>println!</code> mesmo em testes passando</li>

<li><code>cargo test -- --show-output</code> — alias moderno do acima</li>

</ul>

<p>Use <code>cargo test --release</code> para compilar otimizado (mais lento na compilação, mais rápido na execução). Perfeito para suites grandes ou testes de performance.</p>

<h3>Boas práticas com Cargo</h3>

<p>Organize seus testes de integração em subdiretórios lógicos dentro de <code>tests/</code>. Por exemplo: <code>tests/operacoes/</code> e <code>tests/validacoes/</code> com um <code>mod.rs</code> em cada. Isso mantém a estrutura clara conforme sua suite cresce. Lembre-se: código em <code>tests/</code> é compilado como crates independentes, então cada arquivo precisa ser autocompleto.</p>

<h2>Conclusão</h2>

<p>Aprendemos três pilares fundamentais: <strong>(1) testes unitários usam <code>#[cfg(test)]</code> no mesmo arquivo, oferecendo isolamento e velocidade</strong>; <strong>(2) testes de integração ficam em <code>tests/</code> e testam a API pública como um usuário externo faria</strong>; <strong>(3) <code>cargo test</code> oferece controle fino sobre execução — filtros, paralelização, output — tudo integrado</strong>. Combine essas três práticas com disciplina e você terá confiança máxima no seu código Rust.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://doc.rust-lang.org/book/ch11-00-testing.html" target="_blank" rel="noopener noreferrer">The Rust Programming Language - Testing</a></li>

<li><a href="https://doc.rust-lang.org/cargo/commands/cargo-test.html" target="_blank" rel="noopener noreferrer">Cargo - Tests</a></li>

<li><a href="https://doc.rust-lang.org/rust-by-example/testing.html" target="_blank" rel="noopener noreferrer">Rust by Example - Testing</a></li>

<li><a href="https://www.oreilly.com/library/view/effective-rust/9781098105891/" target="_blank" rel="noopener noreferrer">Effective Rust - Item 3: Testing</a></li>

<li><a href="https://doc.rust-lang.org/book/ch11-03-test-organization.html" target="_blank" rel="noopener noreferrer">Test Organization - Rust Official Documentation</a></li>

</ul>

Comentários

Mais em Rust

Como Usar Workspaces no Cargo: Organizando Projetos Grandes em Rust em Produção
Como Usar Workspaces no Cargo: Organizando Projetos Grandes em Rust em Produção

O que são Workspaces no Cargo? Um workspace no Cargo é um mecanismo para orga...

Como Usar Autenticação JWT em APIs Rust com Axum em Produção
Como Usar Autenticação JWT em APIs Rust com Axum em Produção

Fundamentos de JWT e Segurança em APIs JSON Web Token (JWT) é um padrão abert...

O que Todo Dev Deve Saber sobre Boas Práticas, Clippy e Rustfmt: Código Rust Idiomático
O que Todo Dev Deve Saber sobre Boas Práticas, Clippy e Rustfmt: Código Rust Idiomático

O que é Código Idiomático em Rust? Código idiomático em Rust significa escrev...