Tutorial 9 - Hora dos Testes

A maior parte do desenvolvimento de software não envolve a gravação de um novo código, mas sim a modificação do código existente. Garantir que o código existente continue a funcionar da maneira esperada é uma parte fundamental do processo de desenvolvimento de software. Uma maneira de garantir o comportamento do nosso aplicativo é com um conjunto de testes.

Executar o conjunto de testes

Acontece que nosso projeto já tem um conjunto de testes! Quando geramos nosso projeto originalmente, foram gerados dois diretórios de nível superior: src e tests. A pasta src contém o código do nosso aplicativo; a pasta tests contém nosso conjunto de testes. Dentro da pasta tests há um arquivo chamado test_app.py com o seguinte conteúdo:

def test_first():
    "An initial test for the app"
    assert 1 + 1 == 2

Este é um Pytest caso de teste - um bloco de código que pode ser executado para verificar algum comportamento do seu aplicativo. Nesse caso, o teste é um espaço reservado e não testa nada sobre nosso aplicativo, mas é um teste que podemos executar.

Podemos executar esse conjunto de testes usando a opção --test do briefcase dev. Como esta é a primeira vez que estamos executando testes, também precisamos passar a opção -r para garantir que os requisitos de teste também sejam instalados:

(beeware-venv) $ briefcase dev --test -r

[helloworld] Installing requirements...
...
Installing dev requirements... done

[helloworld] Running test suite in dev environment...
===========================================================================
============================= test session starts ==============================
platform darwin -- Python 3.11.0, pytest-7.2.0, pluggy-1.0.0 -- /Users/brutus/beeware-tutorial/beeware-venv/bin/python3.11
cachedir: /var/folders/b_/khqk71xd45d049kxc_59ltp80000gn/T/.pytest_cache
rootdir: /Users/brutus
plugins: anyio-3.6.2
collecting ... collected 1 item

tests/test_app.py::test_first PASSED                                     [100%]

============================== 1 passed in 0.01s ===============================

Sucesso! Acabamos de executar um único teste que verifica se a matemática Python funciona da maneira esperada (que alívio!).

Vamos substituir esse teste de espaço reservado por um teste para verificar se o nosso método greeting() se comporta da maneira esperada. Substitua o conteúdo de test_app.py pelo seguinte:

from helloworld.app import greeting


def test_name():
    """If a name is provided, the greeting includes the name"""

    assert greeting("Alice") == "Hello, Alice"


def test_empty():
    """If a name is not provided, a generic greeting is provided"""

    assert greeting("") == "Hello, stranger"

Isso define dois novos testes, verificando os dois comportamentos que esperamos ver: a saída quando um nome é fornecido e a saída quando o nome está vazio.

Agora podemos executar novamente o conjunto de testes. Dessa vez, não precisamos fornecer a opção -r, pois os requisitos de teste já foram instalados; precisamos apenas usar a opção --test:

(beeware-venv) $ briefcase dev --test

[helloworld] Running test suite in dev environment...
===========================================================================
============================= test session starts ==============================
...
collecting ... collected 2 items

tests/test_app.py::test_name PASSED                                      [ 50%]
tests/test_app.py::test_empty PASSED                                     [100%]

============================== 2 passed in 0.11s ===============================

Excelente! Nosso método utilitário greeting() está funcionando como esperado.

Desenvolvimento orientado por testes

Agora que temos um conjunto de testes, podemos usá-lo para impulsionar o desenvolvimento de novos recursos. Vamos modificar nosso aplicativo para ter uma saudação especial para um determinado usuário. Podemos começar adicionando um caso de teste para o novo comportamento que gostaríamos de ver na parte inferior do test_app.py:

def test_brutus():
    """If the name is Brutus, a special greeting is provided"""

    assert greeting("Brutus") == "BeeWare the IDEs of Python!"

Em seguida, execute o conjunto de testes com esse novo teste:

(beeware-venv) $ briefcase dev --test

[helloworld] Running test suite in dev environment...
===========================================================================
============================= test session starts ==============================
...
collecting ... collected 3 items

tests/test_app.py::test_name PASSED                                      [ 33%]
tests/test_app.py::test_empty PASSED                                     [ 66%]
tests/test_app.py::test_brutus FAILED                                    [100%]

=================================== FAILURES ===================================
_________________________________ test_brutus __________________________________

    def test_brutus():
        """If the name is Brutus, a special greeting is provided"""

>       assert greeting("Brutus") == "BeeWare the IDEs of Python!"
E       AssertionError: assert 'Hello, Brutus' == 'BeeWare the IDEs of Python!'
E         - BeeWare the IDEs of Python!
E         + Hello, Brutus

tests/test_app.py:19: AssertionError
=========================== short test summary info ============================
FAILED tests/test_app.py::test_brutus - AssertionError: assert 'Hello, Brutus...
========================= 1 failed, 2 passed in 0.14s ==========================

Dessa vez, vemos uma falha no teste - e a saída explica a origem da falha: o teste está esperando a saída «BeeWare the IDEs of Python!», mas nossa implementação de greeting() está retornando «Hello, Brutus». Vamos modificar a implementação de greeting() em src/helloworld/app.py para obter o novo comportamento:

def greeting(name):
    if name:
        if name == "Brutus":
            return "BeeWare the IDEs of Python!"
        else:
            return f"Hello, {name}"
    else:
        return "Hello, stranger"

Se executarmos os testes novamente, veremos que nossos testes foram aprovados:

(beeware-venv) $ briefcase dev --test

[helloworld] Running test suite in dev environment...
===========================================================================
============================= test session starts ==============================
...
collecting ... collected 3 items

tests/test_app.py::test_name PASSED                                      [ 33%]
tests/test_app.py::test_empty PASSED                                     [ 66%]
tests/test_app.py::test_brutus PASSED                                    [100%]

============================== 3 passed in 0.15s ===============================

Testes de tempo de execução

Até o momento, estamos executando os testes no modo de desenvolvimento. Isso é especialmente útil quando se está desenvolvendo novos recursos, pois é possível iterar rapidamente na adição de testes e na adição de código para que esses testes sejam aprovados. No entanto, em algum momento, você desejará verificar se o seu código também é executado corretamente no ambiente do aplicativo de pacote.

As opções --test e -r também podem ser passadas para o comando run. Se você usar briefcase run --test -r, o mesmo conjunto de testes será executado, mas dentro do pacote de aplicativos empacotados, e não no seu ambiente de desenvolvimento:

(beeware-venv) $ briefcase run --test -r

[helloworld] Updating application code...
Installing src/helloworld... done
Installing tests... done

[helloworld] Updating requirements...
...
[helloworld] Built build/helloworld/macos/app/Hello World.app (test mode)

[helloworld] Starting test suite...
===========================================================================
Configuring isolated Python...
Pre-initializing Python runtime...
PythonHome: /Users/brutus/beeware-tutorial/helloworld/macOS/app/Hello World/Hello World.app/Contents/Resources/support/python-stdlib
PYTHONPATH:
- /Users/brutus/beeware-tutorial/helloworld/macOS/app/Hello World/Hello World.app/Contents/Resources/support/python311.zip
- /Users/brutus/beeware-tutorial/helloworld/macOS/app/Hello World/Hello World.app/Contents/Resources/support/python-stdlib
- /Users/brutus/beeware-tutorial/helloworld/macOS/app/Hello World/Hello World.app/Contents/Resources/support/python-stdlib/lib-dynload
- /Users/brutus/beeware-tutorial/helloworld/macOS/app/Hello World/Hello World.app/Contents/Resources/app_packages
- /Users/brutus/beeware-tutorial/helloworld/macOS/app/Hello World/Hello World.app/Contents/Resources/app
Configure argc/argv...
Initializing Python runtime...
Installing Python NSLog handler...
Running app module: tests.helloworld
---------------------------------------------------------------------------
============================= test session starts ==============================
...
collecting ... collected 3 items

tests/test_app.py::test_name PASSED [ 33%]
tests/test_app.py::test_empty PASSED [ 66%]
tests/test_app.py::test_brutus PASSED [100%]

============================== 3 passed in 0.21s ===============================

[helloworld] Test suite passed!

Como no caso do briefcase dev --test, a opção -r só é necessária na primeira vez em que você executa o conjunto de testes para garantir que as dependências de teste estejam presentes. Nas execuções subsequentes, você pode omitir essa opção.

Você também pode usar a opção --test em back-ends móveis: - assim, briefcase run iOS --test e briefcase run android --test funcionarão, executando o conjunto de testes no dispositivo móvel que você selecionar.

Próximos passos

We’ve now got a a test suite for our application. But it still looks like a tutorial app. Is there anything we can do about that? Turn to Tutorial 10 to find out…