Testar no percurso de Arturo

Aprende a testar os teus exercícios de Arturo no Exercism


O percurso de Arturo usa o pacote de testes unitários Unitt 3.0.0 em todos os testes dos exercícios. Este pacote fornece um framework de testes que suporta tanto a sintaxe moderna, inspirada no RSpec, como a sintaxe antiga, inspirada no XUnit. Executar arturo tester.art ou exercism test dentro da tua pasta local do exercício deve instalar o pacote a partir da Internet na primeira execução. Em alternativa, podes instalar o Unitt executando arturo --package install unitt 3.0.0. Apenas o primeiro teste é executado e o respetivo resultado é-te apresentado. Assim que esse teste passar, reativa o teste seguinte e volta a executar os testes até que todos passem. Tanto na configuração RSpec como na XUnit, basta remover o .skip de it.skip ou de test.skip, respetivamente. Quando todos os testes passarem, submete a tua solução, mas tem em atenção que o executor de testes corre todos os testes, mesmo os que estão ignorados.

Primeiros passos

A pasta do exercício que descarregaste terá vários ficheiros, mas estes são os três essenciais.

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

Executar arturo tester.art configura o Unitt e depois executa os testes dentro de tests/test-leap.art. Os testes importam src/leap.art, que contém a tua solução, e depois fazem uma série de asserções sobre o que a tua solução deve devolver para determinados valores de entrada.

Ficheiro da solução

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

Quando começas um exercício, o ficheiro da tua solução vem com um modelo que contém a estrutura básica do que é esperado. Neste exemplo do Leap, o ficheiro contém uma definição mínima da função isLeap? esperada e, em particular, um panic que interrompe a execução dos testes. Remove a linha do panic e começa a escrever a tua implementação de isLeap?.

Estrutura dos testes 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. Estes blocos podem ser aninhados.
  • it e it.skip definem ambos um único caso de teste, que é executado ou ignorado, respetivamente.
  • expects introduz a asserção que está no centro 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 suite de testes faz uma asserção de que a função isLeap? deve devolver false para o ano 2015. Outra forma de escrever isso é expects.be:'equal? @[false isLeapYear? 2015], em que afirmamos que o valor devolvido por isLeapYear? 2015 é igual a false. Esta forma mais longa, que compara dois valores, é o tipo de asserção mais comum no percurso de Arturo. O valor esperado vem sempre primeiro no bloco, antes do valor do resultado.

Estrutura dos testes 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 ao estilo XUnit, introduzida na v1 do Unitt, é suportada por compatibilidade com versões anteriores e é, em grande parte, equivalente à API moderna ao estilo RSpec, exceto pelas suas asserções menos flexíveis.

  • suite define um bloco de casos de teste que validam algo em comum. Estes blocos podem ser aninhados.
  • test e test.skip definem ambos um único caso de teste, que é executado ou ignorado, respetivamente.
  • assert introduz a asserção que está no centro de um caso de teste. A expressão à direita do -> seguinte tem de ser verdadeira.

Nos testes XUnit, normalmente criam-se variáveis que representam o resultado e, opcionalmente, o valor esperado, antes da linha do assert.

Resultado dos testes

Quer utilizes a API RSpec quer a XUnit, o resultado dos testes será bastante semelhante. A única diferença será o que é impresso por baixo de cada nome de teste. O XUnit reporta a asserção com os valores avaliados, por isso vês false = false. O RSpec, no entanto, reporta-a como false? false, porque usámos a função false? com o expects.

Começando pelo início, o Arturo reporta um erro se o panic do ficheiro inicial não tiver sido removido e não avalia nenhum teste.

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

Assim que o panic for substituído, o Arturo reporta o estado de cada asserção feita dentro de um teste. Agora o primeiro teste passa, mas o segundo é 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 de reativares manualmente o segundo teste, ambos os testes são executados. No entanto, a implementação atual do código de isLeap? só devolve false, por isso o segundo teste falha a 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

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

Este processo continua à medida que reativas mais testes. Quando todos os testes passarem, terminaste o exercício e podes submeter a solução. Tem em atenção que o executor de testes corre todos os testes, estejam eles ignorados ou não, localmente.