No desenvolvimento de jogos, os testes manuais podem se tornar rapidamente repetitivos e propensos a erros. Você já se viu em um desses ciclos de testes aparentemente intermináveis enquanto trabalhava em um novo recurso ou tentava corrigir um bug?
Ao automatizar o teste de código, você pode dedicar mais tempo ao desenvolvimento criativo do jogo e menos às tarefas repetitivas (mas importantes) de controle de qualidade, que garantem que a adição, a remoção ou a alteração do código não prejudique o projeto.
O Unity ajuda você a criar, gerenciar e executar testes automatizados para seus jogos com o Unity Test Framework.
O Unity Test Framework (UTF) permite que você teste o código do seu projeto nos modos Edit e Play. Você também pode direcionar o código de teste para várias plataformas, como autônoma, iOS ou Android.
O UTF é instalado adicionando-o ao seu projeto com o Package Manager.
Por trás disso, o UTF se integra ao NUnit, que é uma conhecida biblioteca de testes de código aberto para linguagens .NET.
Há duas categorias principais de testes que você pode escrever com UTF: o modo Editar e o modo Reproduzir:
Os testes do modo de edição são executados no Unity Editor e têm acesso ao código do editor e do jogo. Isso significa que você pode testar suas extensões personalizadas do Editor ou usar testes para modificar as configurações no Editor e entrar no modo Play, o que é útil para ajustar os valores do Inspector e depois executar testes automatizados com muitas configurações diferentes.
Os testes do modo de jogo permitem que você exercite o código do jogo em tempo de execução. Os testes geralmente são executados como corrotinas usando o atributo [UnityTest]. Isso permite que você teste o código que pode ser executado em vários quadros. Por padrão, os testes do modo Play serão executados no Editor, mas você também pode executá-los em uma compilação de player autônoma para várias plataformas de destino.
Para seguir este exemplo, você precisará instalar o pacote Starter Assets - Third Person Character Controller da Unity Asset Store e importá-lo para um novo projeto.
Instale o UTF via Window > Package Manager. Procure o Test Framework no Registro do Unity no Package Manager. Certifique-se de selecionar a versão 1.3.3 (a versão mais recente no momento da redação deste documento).
Depois que o UTF estiver instalado, abra o arquivo Packages/manifest.json com um editor de texto e adicione uma seção de testáveis após as dependências, assim:
,
"testables": [
"com.unity.inputsystem"
]
Salve o arquivo. Isso será útil mais tarde, quando você precisar fazer referência ao conjunto Unity.InputSystem.TestFramework para testar e emular a entrada do jogador.
Retorne ao Editor e permita que a versão mais recente seja instalada.
Clique em Window > General > Test Runner para exibir a janela do editor do Test Runner.
Nesta parte do tutorial, o foco será a criação de testes no modo Play. Em vez de usar as opções Criar pasta de conjunto de teste na janela do Executor de teste, você as criará usando a janela Projeto.
Com a raiz da pasta Project Assets destacada, clique com o botão direito do mouse e escolha Create > Testing > Tests Assembly Folder.
Uma pasta de projeto Tests é adicionada, contendo um arquivo Tests.asmdef (definição de montagem). Isso é necessário para que os testes façam referência aos módulos e às dependências do jogo.
O código do Character Controller será referenciado nos testes e também precisará de uma definição de montagem. Em seguida, você configurará algumas definições e referências de montagem para facilitar os testes entre os módulos.
Clique com o botão direito do mouse na pasta de projeto Assets/StarterAssets/InputSystem e escolha Create > Assembly Definition. Dê a ele um nome descritivo, por exemplo, StarterAssetsInputSystem.
Selecione o novo arquivo StarterAssetsInputSystem.asmdef e, usando o Inspector, adicione uma referência de definição de montagem a Unity.InputSystem. Clique em Aplicar.
Clique com o botão direito do mouse na pasta do projeto Assets/StarterAssets/ThirdPersonController/Scripts e escolha Create > Assembly Definition. Dê a ele um nome descritivo, por exemplo, ThirdPersonControllerMain.
Como você fez com a definição de montagem anterior, abra ThirdPersonControllerMain no Inspector e selecione as referências para:
- Unity.InputSystem
- StarterAssetsInputSystem
Clique em Aplicar.
Para emular partes do sistema de entrada, você precisará fazer referência a ele em seus testes. Além disso, você precisará fazer referência ao namespace StarterAssets em um assembly que criará para o código do Controlador de terceira pessoa.
Abra o arquivo Tests.asmdef no Inspector e adicione uma referência às seguintes definições de montagem:
- UnityEngine.TestRunner
- UnityEditor.TestRunner
- Unity.InputSystem
- Unity.InputSystem.TestFramework
- ThirdPersonControllerMain
Clique em Aplicar.
Seu primeiro teste abrangerá algumas noções básicas sobre como carregar e mover o personagem principal do pacote Third Person Controller.
Comece configurando o novo projeto com uma cena de ambiente de teste simples e um recurso Prefab de personagem para trabalhar.
Abra a cena chamada Assets/StarterAssets/ThirdPersonController/Scenes/Playground.unity e salve uma cópia dela usando o menu File > Save As (Arquivo > Salvar como ) nesse novo caminho: Assets/Scenes/SimpleTesting.unity
Se você notar materiais cor-de-rosa na visualização do jogo, use o Render Pipeline Converter para atualizar os materiais do Pipeline de Renderização Integrado para o Pipeline de Renderização Universal (URP). Consulte este artigo para obter uma visão geral rápida.
Crie uma nova pasta na pasta Project Assets chamada Resources. Observação: O nome da pasta "Resources" é importante aqui para permitir que o método Unity Resources.Load() seja usado.
Arraste e solte o GameObject PlayerArmature da visualização Scene na nova pasta Resources e escolha criar um Prefab Original quando solicitado. Renomeie o ativo Prefab Character.
Esse será o Prefab de caractere básico usado em seus testes daqui para frente.
Remova o GameObject PlayerArmature da nova cena SimpleTesting e salve as alterações na cena.
Para a última etapa da configuração inicial do teste, vá para Arquivo > Configurações de compilação e escolha Adicionar cenas abertas para adicionar a cena Scenes/SimpleTesting às configurações de compilação.
Selecione a pasta Tests (Testes) na pasta Project Assets (Ativos do projeto). Clique com o botão direito do mouse e escolha Criar > Testes > Script de teste C#.
Nomeie o novo script como CharacterTests. Abra o script em seu IDE para dar uma olhada mais de perto.
Dois stubs de método são fornecidos com o arquivo de classe inicial, demonstrando alguns princípios básicos de teste.
Em seguida, você garantirá que os testes carreguem uma cena de jogo "focada em testes". Essa deve ser uma cena que contenha o mínimo necessário para testar o sistema ou o componente no qual você está se concentrando.
Atualize a classe CharacterTests para adicionar duas novas instruções de uso e implementar a classe InputTestFixture:
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;
public class CharacterTests : InputTestFixture
Adicione dois campos privados à parte superior da classe CharacterTests:
GameObject character = Resources.Load<GameObject>("Character");
Teclado;
O campo de caractere armazenará uma referência ao Character Prefab, carregado da pasta Resources. Keyboard manterá uma referência ao dispositivo de entrada Keyboard fornecido pelo InputSystem.
Substitua o método Setup() da classe InputTestFixture básica fornecendo o seu próprio método na classe CharacterTests:
public override void Setup()
{
SceneManager.LoadScene("Scenes/SimpleTesting");
base.Setup();
keyboard = InputSystem.AddDevice<Keyboard>();
var mouse = InputSystem.AddDevice<Mouse>();
Press(mouse.rightButton);
Release(mouse.rightButton);;
}
O método Setup() executa o método Setup() da classe base e, em seguida, configura sua própria classe CharacterTests carregando a cena de teste e inicializando o dispositivo de entrada do teclado.
A entrada do mouse é adicionada apenas para que o controlador de terceira pessoa comece a receber entrada do dispositivo de teclado simulado/virtual. Isso é quase como uma ação de "definir foco".
Em seu primeiro teste, você instanciará o caractere do Prefab e afirmará que ele não é nulo. Adicione o seguinte método à sua classe de teste:
[Test]
public void TestPlayerInstantiation()
{
GameObject characterInstance = GameObject.Instantiate(character, Vector3.zero, Quaternion.identity);
Assert.That(characterInstance, !Is.Null);
}
Enquanto estiver lá, talvez você queira limpar os métodos de teste do modelo de amostra. Remova os métodos CharacterTestsSimplePasses e CharacterTestsWithEnumeratorPasses.
Salve o script e volte para a janela do Test Runner no Editor. Destaque o teste TestPlayerInstantiation e clique em Run Selected (Executar selecionado).
A marca de verificação verde significa que o teste foi aprovado. Você afirmou que o personagem pode ser carregado a partir de recursos, instanciado na cena de teste e não é nulo nesse ponto.
Você deve ter notado que a anotação [Test] foi usada para esse teste em vez da anotação [UnityTest]. O atributo UnityTest permite que as corrotinas executem testes em vários quadros. Nesse caso, você só precisa instanciar o caractere e afirmar que ele foi carregado.
Em geral, você deve usar o atributo NUnit Test em vez do atributo UnityTest no modo Edit, a menos que precise dar instruções especiais, pular um quadro ou esperar um determinado período de tempo no modo Play.
Em seguida, você usará o UnityTest para afirmar que manter pressionada a tecla de controle para frente faz o personagem avançar.
Adicione o novo método de teste fornecido abaixo à sua classe CharacterTests.
Dois novos métodos auxiliares de teste apareceram: Press() e Release(). Ambos são fornecidos pela classe base InputTestFixture e ajudam a emular o pressionamento e a liberação do controle InputSystem.
O método TestPlayerMoves() faz o seguinte:
Instancia uma instância do caractere do Prefab de caractere no local (X: 0, Y: 0, Z: 0)
Pressiona a tecla de seta para cima no teclado virtual por 1 segundo e depois a solta
Aguarda mais 1 segundo (para que o personagem diminua a velocidade e pare de se mover)
Afirma que o caractere foi movido para uma posição no eixo Z maior que 1,5 unidade.
Salve o arquivo, retorne ao Executor de Testes e execute o novo teste.
Em seguida, você testará um script Monobehaviour personalizado adicionando um componente Player Health simples.
Crie um novo script em Assets/StarterAssets/ThirdPersonController/Scripts. Dê a ele o nome de PlayerHealth.
Abra o script em seu IDE e substitua o conteúdo pelo código fornecido abaixo.
Há muitos códigos novos adicionados aqui. Para resumir, esse script determinará se o personagem do jogador está em um estado de queda. Se o chão for atingido uma vez em um estado de queda, a saúde do personagem será reduzida em 10%.
Localize o Character Prefab em Assets/Resources (Ativos/Recursos). Abra o Prefab e adicione o novo componente de script PlayerHealth.
Em seguida, você usará a cena de teste para afirmar que a saúde do jogador diminui depois de cair de uma borda.
Usando o atributo [UnityTest], você pode escrever um teste do modo Play que testa o dano de queda. Ao cair por mais de 0,2 segundo, o jogador deve receber 0,1f de dano (o equivalente a 10% da saúde máxima).
Na cena SimpleTesting, você verá uma escada que leva a uma saliência. Essa é uma plataforma de teste para gerar o personagem em cima e testar o script PlayerHealth.
Abra o arquivo CharacterTests.cs novamente e adicione um novo método de teste chamado TestPlayerFallDamage:
[UnityTest]
public IEnumerator TestPlayerFallDamage()
{
// gerar o personagem em uma área alta o suficiente na cena de teste
GameObject characterInstance = GameObject.Instantiate(character, new Vector3(0f, 4f, 17.2f), Quaternion.identity);
// Obter uma referência para o componente PlayerHealth e afirmar que a saúde está completa (1f)
var characterHealth = characterInstance.GetComponent<PlayerHealth>();
Assert.That(characterHealth.Health, Is.EqualTo(1f));
// Saia da borda e aguarde a queda
Press(keyboard.upArrowKey);
yield return new WaitForSeconds(0.5f);
Release(keyboard.upArrowKey);
yield return new WaitForSeconds(2f);
// Afirmar que 1 ponto de saúde foi perdido devido ao dano da queda
Assert.That(characterHealth.Health, Is.EqualTo(0.9f));
}
Você também precisará adicionar uma referência de uso ao namespace StarterAssets na parte superior do arquivo de classe:
using StarterAssets;
O teste acima segue um padrão típico de organizar, agir, afirmar (AAA), comumente encontrado em testes:
A seção Arrange de um método de teste de unidade inicializa objetos e define o valor dos dados que são passados para o método em teste.
A seção Act invoca o método em teste com os parâmetros organizados. Nesse caso, a invocação do método em teste é tratada por uma interação física quando o jogador atinge o chão depois de cair.
A seção Assert verifica se a ação do método em teste se comporta conforme o esperado.
De volta ao Editor, execute o novo teste. Ao correr no modo Play, você verá o personagem sair da borda, cair (ultrapassando o limite de 0,2 segundo para categorizar uma queda) e receber dano após atingir o chão.
Os testes não servem apenas para verificar se as alterações no código não quebram a funcionalidade, mas também podem servir como documentação ou indicadores para ajudar os desenvolvedores a pensar em outros aspectos do jogo ao ajustar as configurações.
Depois de começar a criar um conjunto de testes, a próxima etapa é executá-los automaticamente após a conclusão das compilações. Os testes automatizados de unidade e integração executados após a compilação são úteis para detectar regressões ou bugs o mais cedo possível. Eles também podem ser executados como parte de um sistema de compilação automatizado remoto na nuvem.
Muitas vezes, você desejará capturar os resultados das execuções de teste em um formato personalizado para que os resultados possam ser compartilhados com um público mais amplo. Para capturar os resultados dos testes fora do Unity Editor, você precisará dividir os processos de compilação e execução.
Crie um novo script na pasta do projeto Tests chamado SetupPlaymodeTestPlayer.
A classe SetupPlaymodeTestPlayer implementará a interface ITestPlayerBuildModifier. Você usará isso para substituir e "enganchar" no método ModifyOptions, que recebe as opções de jogador da compilação e permite que você as modifique.
usando System.IO;
using UnityEditor;
using UnityEditor.TestTools;
[montagem: TestPlayerBuildModifier(typeof(SetupPlaymodeTestPlayer))]
public class SetupPlaymodeTestPlayer : ITestPlayerBuildModifier
{
public BuildPlayerOptions ModifyOptions(BuildPlayerOptions playerOptions)
{
playerOptions.options &= ~(BuildOptions.AutoRunPlayer | BuildOptions.ConnectToHost);
var buildLocation = Path.GetFullPath("TestPlayers");
var fileName = Path.GetFileName(playerOptions.locationPathName);
if (!string.IsNullOrEmpty(fileName))
buildLocation = Path.Combine(buildLocation, fileName);
playerOptions.locationPathName = buildLocation;
return playerOptions;
}
}
Esse script personalizado do modificador Player Build faz o seguinte quando os testes são executados no modo Play (Local de execução): No jogador):
Desativa a execução automática para jogadores criados e ignora a opção de jogador que tenta se conectar ao host em que está sendo executado
Altera o local do caminho de compilação para um caminho dedicado dentro do projeto(TestPlayers)
Com isso concluído, agora você pode esperar que as compilações estejam localizadas na pasta TestPlayers sempre que terminarem de ser compiladas. Isso agora conclui as modificações de compilação e rompe o vínculo entre a compilação e a execução.
Em seguida, você implementará o relatório de resultados. Isso permitirá que você grave os resultados dos testes em um local personalizado, pronto para a geração e publicação automatizadas de relatórios.
Crie um novo script na pasta do projeto Tests chamado ResultSerializer (fornecido abaixo). Essa classe usará uma referência de montagem para TestRunCallback e implementará a interface ITestRunCallback.
Essa implementação do ITestRunCallback inclui um método RunFinished personalizado, que é o que configura uma compilação de player com testes para gravar os resultados do teste em um arquivo XML chamado testresults.xml.
Com SetupPlaymodeTestPlayer.cs e ResultSerializer.cs combinados, os processos de compilação e execução agora estão divididos. A execução dos testes produzirá os resultados em testresults.xml, localizado no local Application.persistentDataPath da plataforma do player.
Para usar alguns dos tipos dessas classes de gancho, você precisará adicionar uma referência extra ao Tests.asmdef. Atualize-o para adicionar a referência da definição do conjunto UnityEditor.UI.EditorTests.
A execução dos testes no Player agora produzirá uma saída de compilação do player em seu projeto na pasta TestPlayers e um arquivo testresults.xml no local Application.persistentDataPath.
Curso de estrutura de teste do Unity
O pacote Test Framework inclui um curso de teste com exemplos de exercícios para ajudá-lo a aprender mais sobre testes com o Unity. Certifique-se de obter os arquivos de projeto para o curso usando o Package Manager.
Usando o Package Manager > Packages: Registro do Unity > Estrutura de teste, localize a lista suspensa Amostras e importe os exercícios do curso.
Os exercícios serão importados para seu projeto e localizados em Assets/Samples/Test Framework. Cada exemplo inclui uma pasta de exercícios para você trabalhar, bem como uma solução para comparar seu próprio trabalho à medida que o acompanha.
Controle de qualidade de seu código com UTF
Esta palestra da Unite Copenhagen sobre UTF entra em mais detalhes e oferece alguns outros casos de uso interessantes para a personalização de testes. Não deixe de dar uma olhada para ver o que mais é possível fazer.
Depuração em Unity
Acelere seu fluxo de trabalho de depuração no Unity com artigos sobre:
- Microsoft Visual Studio 2022
- Microsoft Visual Studio Code
E-books técnicos avançados
O Unity fornece vários guias avançados para ajudar os desenvolvedores profissionais a otimizar o código do jogo. Criar um guia de estilo C#: Write cleaner code that scales compila conselhos de especialistas do setor sobre como criar um guia de estilo de código para ajudar sua equipe a desenvolver uma base de código limpa, legível e dimensionável.
Outro guia popular entre nossos usuários é Mais de 70 dicas para aumentar a produtividade com o Unity. Ele está repleto de dicas que economizam tempo para melhorar seu fluxo de trabalho agregado diário com o Unity 2020 LTS, incluindo dicas que até mesmo desenvolvedores experientes podem ter perdido.
Documentação
Explore ainda mais a API mais recente do TestRunner, saiba mais sobre outros atributos personalizados do UTF e descubra outros ciclos de vida para se conectar com a documentação do UTF.
Encontre todos os e-books e artigos avançados da Unity no hub de práticas recomendadas da Unity.