Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

Testes unitários

O Godot Engine permite escrever testes unitários diretamente em C++. O motor integra o framework de testes unitários doctest, que oferece a capacidade de escrever suítes de testes e casos de teste junto ao código de produção, mas como os testes no Godot passam por um ponto de entrada main diferente, os testes residem em um diretório dedicado tests/, que está localizado na raiz do código-fonte do motor.

Suporte a plataforma e alvo

Os testes unitários em C++ podem ser executados nos sistemas operacionais Linux, macOS e Windows.

Os testes só podem ser executados com as ferramentas do editor (tools) ativadas, o que significa que os modelos de exportação (export templates) não podem ser testados atualmente.

Executando testes

Antes que os testes possam ser realmente executados, o motor deve ser compilado com a opção de compilação tests ativada (e qualquer outra opção de compilação que você normalmente usa), já que os testes não são compilados como parte do motor por padrão:

scons tests=yes

Assim que a compilação for concluída, execute os testes com a opção de linha de comando --test:

./bin/<godot_binary> --test

A execução do teste pode ser configurada com as várias opções de linha de comando específicas do doctest. Para obter a lista completa de opções suportadas, execute o comando --test com a opção --help:

./bin/<godot_binary> --test --help

Quaisquer outras opções e argumentos após o comando --test são tratados como argumentos para o doctest.

Nota

Os testes são compilados automaticamente se você usar a opção SCons dev_mode=yes. dev_mode=yes é recomendado se você planeja contribuir para o desenvolvimento do motor, pois ele tratará automaticamente os avisos de compilação como erros. O sistema de integração contínua falhará se qualquer aviso de compilação for detectado, portanto, você deve se esforçar para corrigir todos os avisos antes de abrir um pull request.

Filtrando testes

Por padrão, todos os testes são executados se você não fornecer nenhum argumento extra após o comando --test. Mas se você estiver escrevendo novos testes ou gostaria de ver a saída de asserções bem-sucedidas provenientes desses testes para fins de depuração, você pode executar os testes de interesse com as várias opções de filtragem fornecidas pelo doctest.

A sintaxe de caractere curinga * é suportada para corresponder a qualquer número de caracteres em suítes de teste, casos de teste e nomes de arquivos de origem:

Opções de filtro

Abreviação

Exemplos

--test-suite

-ts

-ts="*[GDScript]*"

--test-case

-tc

-tc="*[String]*"

--source-file

-sf

-sf="*test_color*"

Por exemplo, para executar apenas os testes unitários de String, execute:

./bin/<godot_binary> --test --test-case="*[String]*"

A saída de asserções bem-sucedidas pode ser ativada com a opção --success (-s), e pode ser combinada com qualquer combinação de opções de filtragem acima, por exemplo:

./bin/<godot_binary> --test --source-file="*test_color*" --success

Specific tests can be skipped with corresponding -exclude options. As of now, some tests include random stress tests which take a while to execute. In order to skip those kinds of tests, run the following command:

./bin/<godot_binary> --test --test-case-exclude="*[Stress]*"

Escrevendo testes

As suítes de teste representam arquivos de implementação C++ que devem incluir a macro TEST_FORCE_LINK(). A maioria das suítes de teste está localizada diretamente no diretório tests/.

Todos os arquivos de teste são prefixados com test_, e esta é uma convenção de nomenclatura da qual o sistema de compilação do Godot depende para detectar testes em todo o motor.

Aqui está uma suíte de testes mínima funcional com um único caso de teste escrito:

#include "tests/test_macros.h"

TEST_FORCE_LINK(test_string)

namespace TestString {

TEST_CASE("[String] Hello World!") {
    String hello = "Hello World!";
    CHECK(hello == "Hello World!");
}

} // namespace TestString

Nota

Você pode gerar novos testes rapidamente usando o script create_test.py encontrado no diretório tests/. Este script cria automaticamente um novo arquivo de teste com o código padrão necessário no local apropriado. Para visualizar as instruções de uso, execute o script com a flag -h.

O cabeçalho tests/test_macros.h encapsula tudo o que é necessário para escrever testes unitários em C++ no Godot. Ele inclui macros de asserção e registro do doctest, como CHECK, conforme visto acima, e, claro, as definições para escrever os próprios casos de teste.

Ver também

tests/test_macros.h código-fonte para macros implementadas atualmente e aliases para elas.

Os casos de teste são criados usando a macro do tipo função TEST_CASE. Cada caso de teste deve ter uma breve descrição escrita entre parênteses, incluindo opcionalmente tags personalizadas que permitem filtrar os testes em tempo de execução, como [String], [Stress] etc.

Os casos de teste são escritos em um namespace dedicado. Isso não é obrigatório, mas permite evitar colisões de nomes para quando outras funções auxiliares estáticas são escritas para acomodar os procedimentos de teste repetitivos, como preencher dados de teste comuns para cada teste ou escrever testes parametrizados.

O Godot suporta a escrita de testes por módulo C++. Para instruções sobre como escrever testes de módulo, consulte Escrevendo testes unitários personalizados.

Subcasos

Em situações onde você tem uma configuração comum para vários casos de teste com apenas pequenas variações, os subcasos podem ser muito úteis. Aqui está um exemplo:

TEST_CASE("[SceneTree][Node] Testing node operations with a very simple scene tree") {
    // ... common setup (e.g. creating a scene tree with a few nodes)
    SUBCASE("Move node to specific index") {
        // ... setup and checks for moving a node
    }
    SUBCASE("Remove node at specific index") {
        // ... setup and checks for removing a node
    }
}

Cada SUBCASE faz com que o TEST_CASE seja executado desde o início. Os subcasos podem ser aninhados em uma profundidade arbitrária, mas é aconselhável limitar o aninhamento a não mais do que um nível de profundidade.

Asserções

Uma lista de todas as asserções comumente usadas nos testes do Godot, ordenadas por gravidade.

Asserção

Descrição

REQUIRE

Testa se a condição é verdadeira. Falha o teste inteiro imediatamente se a condição não for verdadeira.

REQUIRE_FALSE

Testa se a condição não é verdadeira. Falha o teste inteiro imediatamente se a condição for verdadeira.

CHECK

Testa se a condição é verdadeira. Marca a execução do teste como falha, mas permite executar outras asserções.

CHECK_FALSE

Testa se a condição não é verdadeira. Marca a execução do teste como falha, mas permite executar outras asserções.

WARN

Testa se a condição é verdadeira. Não falha o teste sob nenhuma circunstância, mas registra um aviso se algo não for verdadeiro.

WARN_FALSE

Testa se a condição não é verdadeira. Não falha o teste sob nenhuma circunstância, mas registra um aviso se algo for verdadeiro.

Todas as asserções acima possuem macros *_MESSAGE correspondentes, que permitem imprimir uma mensagem opcional com a justificativa do que deveria acontecer.

Prefira usar CHECK para asserções autoexplicativas e CHECK_MESSAGE para as mais complexas, se você achar que ela merece uma explicação melhor.

Log

A saída do teste é controlada pelo próprio doctest e não depende de forma alguma da funcionalidade de impressão ou registro do Godot, portanto, é recomendado usar macros dedicadas que permitem registrar a saída do teste em um formato escrito pelo doctest.

Macro

Descrição

MESSAGE

Imprime uma mensagem.

FAIL_CHECK

Marca o teste como falha, mas continua a execução. Pode ser envolvido em condicionais para verificações complexas.

FAIL

Falha o teste imediatamente. Pode ser envolvido em condicionais para verificações complexas.

Diferentes relatores (reporters) podem ser escolhidos em tempo de execução. Por exemplo, aqui está como a saída pode ser redirecionada para um arquivo XML:

./bin/<godot_binary> --test --source-file="*test_validate*" --success --reporters=xml --out=doctest.txt

Ver também

doctest: Macros de log.

Testando caminhos de falha

Às vezes, nem sempre é viável testar um resultado esperado. Com a filosofia de desenvolvimento do Godot de que o motor não deve travar (crash) e deve se recuperar graciosamente sempre que ocorrer um erro não fatal, é importante verificar se esses caminhos de falha são de fato seguros para serem executados sem travar o motor.

O comportamento inesperado pode ser testado da mesma forma que qualquer outra coisa. O único problema que isso cria é que a impressão do erro poluirá desnecessariamente a saída do teste com erros vindos do próprio motor (mesmo que o resultado final seja bem-sucedido).

Para aliviar este problema, use as macros ERR_PRINT_OFF e ERR_PRINT_ON diretamente dentro dos casos de teste para desabilitar temporariamente a saída de erro vinda do motor, por exemplo:

TEST_CASE("[Color] Constructor methods") {
    ERR_PRINT_OFF;
    Color html_invalid = Color::html("invalid");
    ERR_PRINT_ON; // Don't forget to re-enable!

    CHECK_MESSAGE(html_invalid.is_equal_approx(Color()),
        "Invalid HTML notation should result in a Color with the default values.");
}

Tags especiais nos nomes dos casos de teste

Essas tags podem ser adicionadas ao nome do caso de teste para modificar ou estender o ambiente de teste:

Tag

Descrição

[SceneTree]

Necessário para casos de teste que dependem de uma árvore de cena (scene tree) com MessageQueue disponível. Também ativa um servidor de renderização fictício (mock) e o ThemeDB.

[Editor]

Como [SceneTree], mas com infraestrutura adicional relacionada ao editor disponível, como EditorSettings.

[Audio]

Inicializa o AudioServer usando um driver de áudio fictício (mock).

[Navigation2D]

Cria o servidor de navegação 2D padrão e o disponibiliza para testes.

[Navigation3D]

Cria o servidor de navegação 3D padrão e o disponibiliza para testes.

Você pode usá-las juntas para combinar múltiplas extensões de ambiente de teste.

Testando sinais

As seguintes macros podem ser usadas para testar sinais:

Macro

Descrição

SIGNAL_WATCH(object, "signal_name")

Começa a monitorar o sinal especificado no objeto fornecido.

SIGNAL_UNWATCH(object, "signal_name")

Para de monitorar o sinal especificado no objeto fornecido.

SIGNAL_CHECK("signal_name", Vector<Vector<Variant>>)

Verifica os argumentos de todos os sinais emitidos. O vetor externo contém cada sinal emitido, enquanto o vetor interno contém a lista de argumentos para aquele sinal. A ordem dos sinais é significativa.

SIGNAL_CHECK_FALSE("signal_name")

Verifica se o sinal especificado não foi emitido.

SIGNAL_DISCARD("signal_name")

Descarta todos os registros do sinal especificado.

Abaixo está um exemplo demonstrando o uso dessas macros:

//...
SUBCASE("[Timer] Timer process timeout signal must be emitted") {
    SIGNAL_WATCH(test_timer, SNAME("timeout"));
    test_timer->start(0.1);

    SceneTree::get_singleton()->process(0.2);

    Array signal_args;
    signal_args.push_back(Array());

    SIGNAL_CHECK(SNAME("timeout"), signal_args);

    SIGNAL_UNWATCH(test_timer, SNAME("timeout"));
}
//...

Ferramentas de teste

As ferramentas de teste (test tools) são métodos avançados que permitem executar procedimentos arbitrários para facilitar o processo de teste manual e depuração das partes internas do motor.

Essas ferramentas podem ser executadas fornecendo o nome de uma ferramenta após a opção de linha de comando --test. Por exemplo, o módulo GDScript implementa e registra várias ferramentas para ajudar na depuração do tokenizador, analisador sintático (parser) e compilador:

./bin/<godot_binary> --test gdscript-tokenizer test.gd
./bin/<godot_binary> --test gdscript-parser test.gd
./bin/<godot_binary> --test gdscript-compiler test.gd

Se qualquer ferramenta desse tipo for detectada, o restante dos testes unitários será ignorado.

As ferramentas de teste podem ser registradas em qualquer lugar do motor, pois o mecanismo de registro se assemelha muito ao que o doctest fornece ao registrar casos de teste usando a técnica de inicialização dinâmica, mas geralmente elas podem ser registradas nas fontes register_types.cpp correspondentes (por módulo ou core).

Aqui está um exemplo de como o GDScript registra ferramentas de teste em modules/gdscript/register_types.cpp:

#ifdef TESTS_ENABLED
void test_tokenizer() {
    TestGDScript::test(TestGDScript::TestType::TEST_TOKENIZER);
}

void test_parser() {
    TestGDScript::test(TestGDScript::TestType::TEST_PARSER);
}

void test_compiler() {
    TestGDScript::test(TestGDScript::TestType::TEST_COMPILER);
}

REGISTER_TEST_COMMAND("gdscript-tokenizer", &test_tokenizer);
REGISTER_TEST_COMMAND("gdscript-parser", &test_parser);
REGISTER_TEST_COMMAND("gdscript-compiler", &test_compiler);
#endif

A análise personalizada de linha de comando pode ser executada por uma própria ferramenta de teste com a ajuda do método do S.O. get_cmdline_args.

Testes de integração para GDScript

O Godot usa o doctest para evitar regressões no GDScript durante o desenvolvimento. Existem vários tipos de scripts de teste que podem ser escritos:

  • testes para erros esperados;

  • testes para avisos (warnings);

  • testes para recursos (features).

Portanto, o processo de escrita de testes de integração para o GDScript é o seguinte:

  1. Escolha o tipo de script de teste que você gostaria de escrever e crie um novo arquivo GDScript no diretório modules/gdscript/tests/scripts dentro do subdiretório correspondente.

  2. Escreva o código GDScript. O script de teste deve ter uma função chamada test() que não recebe argumentos. Essa função será chamada pelo executor de testes (test runner). O teste não deve ter nenhuma dependência, a menos que ela também faça parte do teste. As classes globais (usando class_name) são registradas antes do início do executor, portanto, elas devem funcionar se necessário.

    Aqui está um exemplo de script de teste:

    func test():
        if true # Missing colon here.
            print("true")
    
  3. Mude para o diretório raiz do repositório de código-fonte do Godot.

    cd godot
    
  4. Gere arquivos *.out para atualizar os resultados esperados a partir da saída:

    bin/<godot_binary> --gdscript-generate-tests modules/gdscript/tests/scripts
    

Você pode adicionar a opção --print-filenames para ver os nomes dos arquivos conforme suas saídas de teste são geradas. Se você estiver trabalhando em um novo recurso que está causando travamentos severos (hard crashes), você pode usar essa opção para encontrar rapidamente qual arquivo de teste causa o travamento e depurar a partir daí.

  1. Execute os testes GDScript com:

    ./bin/<godot_binary> --test --test-suite="*GDScript*"
    

Isso também aceita a opção --print-filenames (veja acima).

Se nenhum erro for impresso e tudo corra bem, você terminou!

Aviso

Certifique-se de que a saída possui os valores esperados antes de enviar um pull request. Se o --gdscript-generate-tests produzir arquivos *.out que não estão relacionados aos testes recém-adicionados, você deve reverter esses arquivos e apenas commitar os arquivos *.out dos novos testes.

Nota

O executor de testes (test runner) do GDScript é voltado para testar a implementação do GDScript, não para testar scripts de usuário nem para testar o motor usando scripts. Recomendamos escrever novos testes para problemas já resolvidos relacionados ao GDScript no GitHub, ou escrever testes para recursos que estão funcionando atualmente.

Nota

Se o seu caso de teste exigir que não haja nenhuma função test() presente dentro do arquivo de script, você pode desativar a seção de tempo de execução do teste nomeando o arquivo de script de forma que ele corresponda ao padrão *.notest.gd. Por exemplo, "test_empty_file.notest.gd".