Testes unitários com testthat
Escreva blocos test_that(), use expectativas e execute testes com devtools::test().
Testes unitários com testthat é uma aula grátis de R Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de R Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de R Academy inclui 4 aulas no total.
Por que fazer testes unitários?
Os testes unitários verificam automaticamente se funções individuais se comportam corretamente. Eles detectam regressões quando você altera o código, servem como documentação executável e dão confiança para refatorar com segurança. O pacote testthat é a estrutura de testes padrão para pacotes R.
Configurando o testthat
usethis::use_testthat() adiciona testthat a Suggests em DESCRIPTION, cria tests/testthat/ e cria o script executor tests/testthat.R. Execute-o uma vez ao inicializar um pacote novo.
# usethis::use_testthat()
#
# Creates:
# tests/
# testthat.R <- runner (do not edit)
# testthat/
# (empty — write test files here)
#
# Updates DESCRIPTION:
# Suggests: testthat (>= 3.0.0)
# Config/testthat/edition: 3Criando um arquivo de testes
usethis::use_test('add') cria tests/testthat/test-add.R. Por convenção, os arquivos de testes recebem nomes como test-{function_name}.R. Cada arquivo reúne os testes de uma função ou funcionalidade.
# usethis::use_test('add') # creates tests/testthat/test-add.R
#
# Content of test-add.R:
# test_that('add() returns correct sum', {
# expect_equal(add(1, 2), 3)
# expect_equal(add(-1, 1), 0)
# expect_equal(add(0.1, 0.2), 0.3, tolerance = 1e-7)
# })Estrutura de test_that()
test_that('description', { ... }) agrupa expectativas relacionadas. A cadeia de caracteres de descrição deve completar a frase 'testar se ...' e ser específica o bastante para ser útil nas mensagens de falha.
# Good test_that descriptions:
# test_that('add() handles negative numbers', { ... })
# test_that('add() recycles length-1 vectors', { ... })
# test_that('add() returns NA when input contains NA', { ... })
#
# Bad (too vague):
# test_that('it works', { ... })
# test_that('test1', { ... })expect_equal() e expect_identical()
expect_equal(actual, expected) testa com uma tolerância numérica para números de ponto flutuante. expect_identical(actual, expected) exige igualdade exata, inclusive quanto ao tipo. Na maioria dos casos, expect_equal() é preferível.
# test_that('add() adds correctly', {
# expect_equal(add(1, 2), 3) # numeric equality
# expect_equal(add(0.1, 0.2), 0.3) # tolerance handles floating point
# expect_identical(add(1L, 2L), 3L) # exact type match: integer
# expect_identical(add(1.0, 2.0), 3.0) # exact type match: double
# })expect_error() e expect_warning()
Verifique se as funções produzem os erros e avisos corretos. Passe um padrão de expressão regular para corresponder à mensagem de erro — isso garante que o erro correto seja gerado, e não apenas qualquer erro.
# test_that('add() validates input types', {
# expect_error(
# add('a', 2),
# regexp = 'numeric' # message must contain 'numeric'
# )
# expect_error(
# add(NULL, 1),
# regexp = 'numeric'
# )
# })
#
# test_that('sqrt() warns on negative input', {
# expect_warning(sqrt(-1))
# })expect_true() e expect_false()
expect_true(expr) e expect_false(expr) testam condições lógicas. Use-as ao testar predicados ou condições que retornam um único valor lógico.
# test_that('is_positive() returns correct logical', {
# expect_true(is_positive(5))
# expect_true(is_positive(0.001))
# expect_false(is_positive(0))
# expect_false(is_positive(-3))
# })
#
# # Also useful for vector tests:
# test_that('add() result has correct length', {
# result <- add(c(1,2,3), c(4,5,6))
# expect_true(length(result) == 3)
# })Mais funções de expectativa
testthat fornece muitas funções de expectativa para diferentes situações:
expect_length(x, n)— verificar o comprimento do vetorexpect_type(x, 'double')— verificar o tipo básicoexpect_s3_class(x, 'data.frame')— verificar a classe S3expect_null(x)— verificar se é NULLexpect_match(string, regexp)— verificar o padrão da cadeia de caracteres
# test_that('add() output has correct type and length', {
# result <- add(c(1.0, 2.0), c(3.0, 4.0))
# expect_type(result, 'double')
# expect_length(result, 2)
# })
#
# test_that('summary_stats() returns a data frame', {
# result <- summary_stats(rnorm(100))
# expect_s3_class(result, 'data.frame')
# })Executando testes com devtools::test()
devtools::test() (Ctrl+Shift+T) executa todos os arquivos de testes e exibe um resumo dos sucessos, falhas e avisos. As falhas de testes individuais mostram a expectativa que falhou e os valores real e esperado.
# devtools::test()
#
# Example output:
# == Testing mypackage ====================================
# v | OK F W S | Context
# v | 3 | add [0.1s]
# v | 4 | subtract [0.1s]
# x | 2 1 | multiply [0.2s]
# -- Failure (test-multiply.R:5): multiply() handles zero
# multiply(5, 0) not equal to 0.
# Actual: 5
# Expected: 0
# ==========================================================
# [ FAIL 1 | WARN 0 | SKIP 0 | PASS 9 ]Cobertura de testes com covr
covr::package_coverage() mede qual porcentagem das linhas do seu pacote é executada pelos testes. covr::report() abre um relatório HTML mostrando as linhas cobertas (em verde) e não cobertas (em vermelho). Procure obter pelo menos 80% de cobertura.
# library(covr)
# cov <- package_coverage()
# print(cov)
#
# Example output:
# mypackage Coverage: 87.50%
# R/add.R: 100.00%
# R/subtract.R: 100.00%
# R/utils.R: 62.50% <- needs more tests!
#
# covr::report() # interactive HTML report
# covr::zero_coverage(cov) # list uncovered linesTestando casos extremos
Bons testes abrangem não apenas o caminho esperado, mas também casos extremos:
- Entradas vazias:
numeric(0),character(0) - Entradas NA: a função propaga ou trata NA?
- Entradas de comprimento 1 versus comprimento n
- Valores-limite: 0, números negativos, valores muito grandes
- Tipos incorretos: o que acontece quando o usuário passa uma cadeia de caracteres para uma função numérica?
# test_that('add() handles edge cases', {
# expect_equal(add(numeric(0), numeric(0)), numeric(0)) # empty
# expect_true(is.na(add(NA, 1))) # NA propagation
# expect_equal(add(1, c(1,2,3)), c(2,3,4)) # recycling
# expect_equal(add(.Machine$integer.max, 0L), # boundary
# .Machine$integer.max)
# })Verificação rápida: expect_error()
Você chama expect_error(my_fn('bad'), regexp = 'invalid input'). O que esse teste verifica?
Recapitulação de testes unitários
Testando um pacote R com testthat:
usethis::use_testthat()— configura a infraestrutura de testes uma vezusethis::use_test('fn')— criatests/testthat/test-fn.Rtest_that('description', {...})— agrupa expectativas relacionadasexpect_equal(),expect_error(),expect_warning(),expect_true(),expect_false()— funções principais de expectativadevtools::test()— executa todos os testes (Ctrl+Shift+T)covr::package_coverage()— mede a cobertura dos testes
Perguntas Frequentes
A aula “Testes unitários com testthat” é grátis?
Sim — o texto completo de “Testes unitários com testthat” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de R Academy, atualize para CoddyKit PRO. O curso de R Academy inclui 4 aulas no total.
O que vou aprender em “Testes unitários com testthat”?
Escreva blocos test_that(), use expectativas e execute testes com devtools::test(). Você pratica R Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar R Academy?
Nenhuma experiência prévia é necessária. R Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.
Quanto tempo leva a aula “Testes unitários com testthat”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de R Academy?
Sim. Cada aula de R Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Estrutura de pacotes com usethis e devtools
- Documentando funções com roxygen2
- Testes unitários com testthat
- Submissão ao CRAN e manutenção de pacotes