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.
Checking the stable version of the documentation...
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 |
|
|
|
|
|
|
|
|
|
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 |
|
Testa se a condição é verdadeira. Falha o teste inteiro imediatamente se a condição não for verdadeira. |
|
Testa se a condição não é verdadeira. Falha o teste inteiro imediatamente se a condição for verdadeira. |
|
Testa se a condição é verdadeira. Marca a execução do teste como falha, mas permite executar outras asserções. |
|
Testa se a condição não é verdadeira. Marca a execução do teste como falha, mas permite executar outras asserções. |
|
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. |
|
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.
Ver também
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 |
|
Imprime uma mensagem. |
|
Marca o teste como falha, mas continua a execução. Pode ser envolvido em condicionais para verificações complexas. |
|
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
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.");
}
Testando sinais
As seguintes macros podem ser usadas para testar sinais:
Macro |
Descrição |
|---|---|
|
Começa a monitorar o sinal especificado no objeto fornecido. |
|
Para de monitorar o sinal especificado no objeto fornecido. |
|
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. |
|
Verifica se o sinal especificado não foi emitido. |
|
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:
Escolha o tipo de script de teste que você gostaria de escrever e crie um novo arquivo GDScript no diretório
modules/gdscript/tests/scriptsdentro do subdiretório correspondente.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 (usandoclass_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")
Mude para o diretório raiz do repositório de código-fonte do Godot.
cd godot
Gere arquivos
*.outpara 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í.
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".