O teste de unidade permitirá verificar a qualidade do código depois que você escrevê-lo, mas também é possível usá-lo para melhorar o processo de desenvolvimento à medida que avança. Em vez de gravar testes depois de concluir o desenvolvimento do aplicativo, considere a possibilidade de gravar os testes conforme você avança. Isso ajuda você a projetar unidades de código pequenas, de fácil manutenção e reutilizáveis. Isso também facilita o teste rápido e completo do código.
Ao fazer o teste de unidade local, você executa testes que permanecem dentro do ambiente de desenvolvimento sem envolver componentes remotos. O App Engine oferece utilitários de teste que usam implementações locais de armazenamento de dados e outros serviços do App Engine. Isso significa ser possível fazer o código usar esses serviços localmente, sem implantá-lo no App Engine, usando-se stubs de serviço.
Stub de serviço é um método que simula o comportamento do serviço. Por exemplo, o stub de serviço do armazenamento de dados mostrado em Como escrever testes de Datastore e Memcache permite testar o código do armazenamento de dados sem fazer solicitações para o armazenamento de dados real. Qualquer entidade armazenada durante um teste de unidade do armazenamento de dados é mantida na memória, e não no armazenamento de dados, e será excluída depois da execução do teste. É possível executar testes pequenos e rápidos sem dependência do próprio armazenamento de dados.
Este documento apresenta informações como configurar uma biblioteca de testes e descreve como escrever testes de unidade em vários serviços do App Engine locais.
Como configurar uma biblioteca de testes
Os utilitários de teste do SDK não estão vinculados a nenhuma biblioteca específica, mas este guia usa JUnit para os exemplos. Dessa maneira, você tem algo concreto e completo para trabalhar. Antes de começar a gravar os testes, você precisará adicionar o JAR JUnit 4 apropriado ao classpath de teste. Depois que fizer isso, você poderá escrever um teste JUnit muito simples.
Se você estiver executando o Eclipse, selecione o arquivo de origem do teste a ser executado. Selecione o menu Executar > Executar como > Teste do JUnit. Os resultados do teste aparecerão na janela do Console.
Como apresentar os utilitários de teste em Java 8
MyFirstTest
demonstra a configuração de teste mais simples possível e, em testes que não dependam das APIs do App Engine ou implementações de serviços locais, talvez você não precise de mais nada. Entretanto, caso os testes ou o código em teste tenham essas dependências, adicione os seguintes arquivos JAR ao classpath de teste:
${SDK_ROOT}/lib/impl/appengine-api.jar
${SDK_ROOT}/lib/impl/appengine-api-stubs.jar
${SDK_ROOT}/lib/appengine-tools-api.jar
Esses JARs disponibilizam as APIs de tempo de execução e as implementações locais dessas APIs para os testes.
Os serviços do App Engine esperam várias coisas do ambiente de execução, e configurá-las envolve uma quantidade razoável de códigos boilerplate. Em vez de você configurar os códigos, é possível usar os utilitários no pacote com.google.appengine.tools.development.testing
. Para usar esse pacote, adicione o seguinte arquivo JAR ao classpath de teste:
${SDK_ROOT}/lib/testing/appengine-testing.jar
Reserve alguns minutos para navegar pelo javadoc do pacote com.google.appengine.tools.development.testing
. A classe mais importante neste pacote é LocalServiceTestHelper, que processa toda a configuração de ambiente necessária e oferece um ponto de configuração de nível superior para todos os serviços locais que você queira acessar nos testes.
Para gravar um teste que acesse um serviço local específico:
- Crie uma instância de
LocalServiceTestHelper
com uma implementaçãoLocalServiceTestConfig
para esse serviço local específico. - Chame
setUp()
na instânciaLocalServiceTestHelper
antes de cada teste etearDown()
depois de cada teste.
Como gravar testes de Memcache e Datastore
O exemplo a seguir testa o uso do serviço datastore.
Neste exemplo, LocalServiceTestHelper
configura e elimina as partes do ambiente de execução comuns a todos os serviços locais, enquanto LocalDatastoreServiceTestConfig
configura e elimina as partes do ambiente de execução específicas ao serviço local do armazenamento de dados. Se você ler o javadoc, verá que isso envolve a configuração do serviço de armazenamento de dados local para manter todos os dados na memória (em vez de liberar para o disco em intervalos regulares) e limpar todos os dados na memória no final de cada teste. Esse é o comportamento padrão para um teste do armazenamento de dados e você pode alterá-lo caso ele não corresponda a suas necessidades.
Como alterar o exemplo para acessar memcache, em vez do armazenamento de dados
Para criar um teste que acesse o serviço memcache local, você pode usar o código mostrado acima, com algumas pequenas alterações.
Em vez de importar classes relacionadas ao armazenamento de dados, importe as relacionadas a memcache. Ainda é necessário importar LocalServiceTestHelper
.
Altere o nome da classe que você cria e altere a instância de LocalServiceTestHelper
para que seja específica do memcache.
E, por fim, altere a maneira como você efetivamente executa o teste. Dessa maneira, ele é relevante para memcache.
Como no exemplo do armazenamento de dados, LocalServiceTestHelper
e o LocalServiceTestConfig
específico do serviço (neste caso, LocalMemcacheServiceTestConfig
) gerenciam o ambiente de execução.
Como escrever testes do Cloud Datastore
Se o app usar o Cloud Datastore, será possível escrever testes que verifiquem o comportamento do app diante de uma consistência eventual. LocalDatastoreServiceTestConfig
expõe opções que facilitam isso:
Definindo a porcentagem de trabalho não aplicado como 100, estamos instruindo o armazenamento de dados local para funcionar com o valor máximo de consistência eventual. Consistência eventual máxima significa que haverá commit das gravações, mas elas sempre deixarão de ser aplicadas. Dessa maneira, consultas globais (não ancestrais) sempre deixarão de ver alterações. Obviamente, isso não representa o volume de consistência eventual que o aplicativo verá em execução na produção, mas, para fins de teste é muito útil ser capaz de configurar o armazenamento de dados local para se comportar assim sempre.
Se quiser ter um controle mais refinado sobre quais transações não são aplicadas, você pode registrar seu próprio HighRepJobPolicy
:
As APIs de teste são úteis para verificar se seu aplicativo se comporta adequadamente
diante de consistência eventual, mas lembre-se que o modelo de consistência de leitura de Alta
replicação local é uma aproximação do modelo de consistência de leitura de Alta
replicação de produção, não uma réplica exata. No ambiente
local, executar um get()
de um Entity
que pertence a um grupo de entidades
com uma gravação não aplicada sempre tornará os resultados da gravação não aplicada
visíveis para as consultas globais subsequentes. Isso não ocorre na produção.
Como escrever testes de fila de tarefas
Os testes que usam a fila de tarefas local são um pouco mais complexos porque, diferentemente do armazenamento de dados e do memcache, a API da fila de tarefas não expõe um recurso para examinar o estado do serviço. Precisamos acessar a fila de tarefas local propriamente dita para verificar se uma tarefa foi programada com os parâmetros esperados. Para fazer isso, precisamos de com.google.appengine.api.taskqueue.dev.LocalTaskQueue
.
Observe como solicitamos a LocalTaskqueueTestConfig
um processamento para a instância de serviço local e depois examinamos o serviço local em si para garantir que a tarefa foi agendada da maneira esperada. Todas as implementações de LocalServiceTestConfig
expõem um método semelhante. Talvez nem sempre seja necessário, mas você agradecerá por ele estar disponível cedo ou tarde.
Como definir o arquivo de configuração queue.xml
As bibliotecas de teste da fila de tarefas permitem que qualquer número de configurações queue.xml
seja especificado por LocalServiceTestHelper por meio do método LocalTaskQueueTestConfig.setQueueXmlPath
. Atualmente, as configurações de limite de taxa de qualquer fila são ignoradas pelo servidor de desenvolvimento local.
Não é possível executar tarefas simultâneas localmente.
Por exemplo, um projeto pode precisar testar o arquivo queue.xml
que será enviado e usado pelo aplicativo do App Engine. Supondo que o arquivo queue.xml
esteja no local padrão, o código de amostra acima pode ser modificado da seguinte maneira para conceder ao teste acesso às filas especificadas no arquivo src/main/webapp/WEB-INF/queue.xml
:
Modifique o caminho para o arquivo queue.xml
de acordo com a estrutura de arquivos do seu projeto.
Use o método QueueFactory.getQueue
para acessar filas por nome:
Como escrever testes de tarefa adiada
Se o código do aplicativo usar tarefas diferidas, os utilitários de teste Java facilitarão a gravação de um teste de integração que verifique os resultados dessas tarefas.
Assim como acontece com o primeiro exemplo da fila de tarefas locais, estamos usando LocalTaskqueueTestConfig
, mas desta vez estamos fazendo a inicialização com alguns argumentos adicionais que nos oferecem uma maneira fácil de verificar não apenas se a tarefa foi agendada, mas se ela foi executada: chamamos setDisableAutoTaskExecution(false)
para solicitar que a fila de tarefas locais execute automaticamente as tarefas. Chamamos setCallbackClass(LocalTaskQueueTestConfig.DeferredTaskCallback.class)
para solicitar que a fila de tarefas locais use um callback que entenda como executar tarefas adiadas. Por fim, chamamos setTaskExecutionLatch(latch)
para solicitar que a fila de tarefas locais solte a trava depois da execução de cada tarefa. Essa configuração permite escrever um teste no qual enfileiramos uma tarefa adiada, aguardar a tarefa ser executada e verificar se ela se comportou conforme o esperado quando foi executada.
Como escrever testes de recursos do serviço local
O teste de recursos envolve alterar o status de um serviço, como armazenamento de dados, blobstore, memcache etc. e executar o aplicativo em relação a esse serviço para determinar se o aplicativo está respondendo conforme o esperado em condições diferentes. O status do recurso pode ser alterado usando-se a classe LocalCapabilitiesServiceTestConfig.
O snippet de código a seguir altera o status do recurso do serviço de armazenamento de dados para desativado e executa um teste no serviço de armazenamento de dados. É possível substituir outros serviços do armazenamento de dados conforme necessário.
O teste de amostra primeiro cria um objeto Capability
inicializado no armazenamento de dados e, em seguida, cria um objeto CapabilityStatus
definido como DISABLED. O LocalCapabilitiesServiceTestConfig
é criado com o recurso e o status definidos usando os objetos Capability
e CapabilityStatus
que acabaram de ser criados.
O LocalServiceHelper
é, então, criado usando o objeto LocalCapabilitiesServiceTestConfig
. Agora que o teste foi configurado, o DatastoreService
é criado e uma Consulta é enviada a ele para determinar se o teste gera os resultados esperados, neste caso, um CapabilityDisabledException
.
Como escrever testes para outros serviços
Há utilitários de teste disponíveis para blobstore e para outros serviços do App Engine. Para ver uma lista de todos os serviços que têm implementações locais para teste, consulte a documentação do LocalServiceTestConfig
.
Como escrever testes com expectativas de autenticação
Este exemplo mostra como escrever testes que verificam a lógica que usa UserService para determinar se um usuário se conectou ou tem privilégios de administrador. Qualquer usuário com o papel básico de leitor, editor ou proprietário, ou o papel predefinido de administrador de aplicativo do App Engine, tem privilégios de administrador.
Neste exemplo, estamos configurando o LocalServiceTestHelper
com o LocalUserServiceTestConfig
para podermos usar o UserService
em nosso teste, mas também estamos configurando alguns dados de ambiente relacionados à autenticação no próprio LocalServiceTestHelper
.
Neste exemplo, estamos configurando o LocalServiceTestHelper
com o LocalUserServiceTestConfig
para que possamos usar o OAuthService
.