Testes na trilha de Arturo

Aprenda a testar seus exercícios de Arturo no Exercism


A trilha de Arturo usa o pacote de testes unitários Unitt 3.0.0 para todos os testes dos exercícios. Esse pacote fornece um framework de testes que suporta tanto a sintaxe moderna inspirada no RSpec quanto a sintaxe legada inspirada no XUnit. Rodar arturo tester.art ou exercism test de dentro da pasta local do seu exercício deve instalar o pacote pela Internet na primeira execução. Como alternativa, você pode instalar o Unitt rodando arturo --package install unitt 3.0.0. Só o primeiro teste será rodado e reportado para você. Quando esse teste passar, reative o próximo e rode os testes de novo até que cada teste passe. Tanto na configuração do RSpec quanto na do XUnit, isso é tão simples quanto remover .skip de it.skip ou de test.skip, respectivamente. Quando todos os testes passarem, envie sua solução, mas fique atento: o test runner roda todos os testes, até os ignorados.

Primeiros passos

A pasta do exercício que você baixou vai ter alguns arquivos, mas estes três são os essenciais.

leap/
├── src/leap.art         # Your solution file
├── tests/test-leap.art  # Test cases for the exercise
└── tester.art           # Test runner entry point

Rodar arturo tester.art vai configurar o Unitt e depois rodar os testes dentro de tests/test-leap.art. Os testes vão importar src/leap.art, que contém sua solução, e então fazer uma série de asserções sobre o que sua solução deve retornar para entradas específicas.

Arquivo da solução

isLeap?: function [year][
    panic "please implement the isLeap? function"
]

Ao começar um exercício, o arquivo da sua solução vem com um template contendo a estrutura básica do que se espera. Neste exemplo do Leap, o arquivo contém uma definição básica da função isLeap? esperada e, em especial, um panic que interrompe a execução dos testes. Remova a linha do panic e então comece a programar sua implementação de isLeap?.

Estrutura de testes do RSpec

import.version:3.0.0 {unitt}!
import {src/leap}!

describe "Leap" [
    it "a year not divisible by 4 is a common year" [
        expects.be:'false? @[isLeap? 2015]
    ]

    it.skip "a year divisible by 4 and not divisible by 100 is a leap year" [
        expects.be:'true? @[isLeap? 1996]
    ]
]
  • describe define um bloco de casos de teste que validam algo em comum. Esses blocos podem ser aninhados.
  • it e it.skip definem cada um um único caso de teste, que é rodado ou ignorado, respectivamente.
  • expects introduz a asserção que está no coração de um caso de teste. expects.be recebe uma função de predicado, que é usada para fazer a asserção.

Em expects.be:'false? @[isLeap? 2015], a suíte de testes está afirmando que a função isLeap? deve retornar false para o ano 2015. Outra forma de escrever isso é expects.be:'equal? @[false isLeapYear? 2015], em que afirmamos que o valor retornado por isLeapYear? 2015 é igual a false. Essa forma mais longa, que compara dois valores, é o tipo de asserção mais comum na trilha de Arturo. O valor esperado sempre vem primeiro no bloco, antes do valor do resultado.

Estrutura de testes do XUnit

suite "Leap" [
    test "a year not divisible by 4 is a common year" [
        result: isLeap? 2015
        assert -> false = result
    ]

    test.skip "a year divisible by 4 and not divisible by 100 is a leap year" [
        result: isLeap? 1996
        assert -> true = result
    ]
]

A API no estilo XUnit, introduzida na v1 do Unitt, é suportada por compatibilidade retroativa e é basicamente equivalente à API moderna no estilo RSpec, exceto pelas asserções menos flexíveis.

  • suite define um bloco de casos de teste que validam algo em comum. Esses blocos podem ser aninhados.
  • test e test.skip definem cada um um único caso de teste, que é rodado ou ignorado, respectivamente.
  • assert introduz a asserção que está no coração de um caso de teste. A expressão do lado direito do -> a seguir precisa ser verdadeira.

Com os testes XUnit, normalmente criamos variáveis que representam o resultado e, opcionalmente, o valor esperado antes da linha do assert.

Saída dos testes

Esteja você usando a API do RSpec ou a do XUnit, a saída dos testes será basicamente parecida. A única diferença vai ser o que é impresso abaixo de cada nome de teste. O XUnit reporta a asserção usando os valores avaliados, então você veria false = false. O RSpec, por sua vez, reporta como false? false, porque usamos a função false? com expects.

Começando do início, o Arturo vai reportar um erro se o panic do arquivo inicial não tiver sido removido e não vai avaliar nenhum teste.

Uncaught error in tests/test-leap.art:
#[
        output: {
                
                ══╡ Program Error ╞═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ <script> ══
                
                  Please implement the isLeap? function
                
                
        }
        code: 1
]

Depois que o panic for substituído, o Arturo vai reportar o status de cada asserção feita dentro de um teste. Agora o primeiro teste passa, mas o segundo teste é ignorado.

======== .unitt/tests/test-leap.art ========

Describe: Leap

    ✅ - assert that a year not divisible by 4 is a common year
         ✅: false? false

    ⏩ - assert that a year divisible by 4 and not divisible by 100 is a leap year
         skipped!


========== Summary ==========

⏏️   TOTAL: 2 assertions
✅  PASSED: 1 assertion
⏩ SKIPPED: 1 assertion
❌  FAILED: 0 assertion

========== ======= ==========

Depois que o segundo teste for reativado manualmente, os dois testes serão rodados. No entanto, a implementação atual de isLeap? só retorna false, então o segundo teste falha na sua asserção.

======== .unitt/tests/test-leap.art ========

Describe: Leap

    ✅ - assert that a year not divisible by 4 is a common year
         ✅: false? false

    ❌ - assert that a year divisible by 4 and not divisible by 100 is a leap year
         ❌: true? false


========== Summary ==========

⏏️   TOTAL: 2 assertions
✅  PASSED: 1 assertion
⏩ SKIPPED: 0 assertion
❌  FAILED: 1 assertion

========== ======= ==========

Esse processo continua conforme você reativa mais testes. Quando todos os testes passarem, você terminou o exercício e pode enviar a solução. Vale notar que o test runner vai rodar todos os testes, estejam eles ignorados ou não localmente.