Escreva um executor de testes Tradefed

Esta página descreve como escrever um novo executor de teste no Tradefed.

Fundo

Se você está curioso sobre o lugar dos executores de teste na arquitetura Tradefed, consulte Estrutura de um executor de teste .

Este não é um pré-requisito para escrever um novo executor de teste; executores de teste podem ser escritos isoladamente.

Mínimo: Implementando a interface

O mínimo para se qualificar como um executor de teste Tradefed é implementar a interface IRemoteTest e, mais especificamente, o run(TestInformation testInfo, ITestInvocationListener listener) .

Esse método é aquele invocado pelo chicote ao usar o executor de teste, semelhante a um Java Runnable.

Cada parte desse método é considerada parte da execução do executor de teste.

Relatório de resultados do executor de teste

O método run na interface base dá acesso a um objeto ouvinte do tipo ITestInvocationListener . Este objeto é a chave para relatar resultados estruturados do executor de teste para o chicote.

Ao relatar resultados estruturados, um executor de teste tem as seguintes propriedades:

  • Relate uma lista adequada de todos os testes executados, quanto tempo eles levaram e se eles passaram individualmente, falharam ou alguns outros estados.
  • Relate as métricas associadas aos testes, se aplicável, por exemplo, métricas de tempo de instalação.
  • Encaixe na maioria das ferramentas de infraestrutura, por exemplo, exibir resultados e métricas, etc.
  • Geralmente mais fácil de depurar, pois há um rastreamento mais granular da execução.

Dito isso, relatar resultados estruturados é opcional; um executor de teste pode simplesmente querer avaliar o estado de toda a execução como APROVADO ou FALHA sem nenhum detalhe da execução real.

NOTA: É mais difícil implementar um corredor que siga a sequência de eventos, mas recomendamos fazê-lo devido aos benefícios listados acima.

Os eventos a seguir podem ser chamados no ouvinte para notificar o chicote sobre o andamento atual das execuções:

  • testRunStarted: Notifica o início de um grupo de casos de teste que estão relacionados entre si.
    • testStarted: Notifica o início do início de um caso de teste.
    • testFailed/testIgnored: Notifica a mudança de estado do caso de teste em andamento. Um caso de teste sem qualquer mudança de estado é considerado aprovado.
    • testEnded: Notifica o fim do caso de teste.
  • testRunFailed: Notifica que o status geral da execução do grupo de casos de teste é uma falha. Uma execução de teste pode ser aprovada ou reprovada independentemente dos resultados dos casos de teste, dependendo do que a execução estava esperando. Por exemplo, um binário executando vários casos de teste pode relatar todos os casos de teste aprovados , mas com um código de saída de erro (por qualquer motivo: arquivos vazados etc.).
  • testRunEnded: Notifica o fim do grupo de casos de teste.

Manter e garantir a ordem adequada dos retornos de chamada é responsabilidade do implementador do executor de teste, por exemplo, garantir que testRunEnded seja chamado em caso de exceção usando uma cláusula finally .

Os retornos de chamada dos casos de teste ( testStarted , testEnded , etc.) são opcionais. Uma execução de teste pode ocorrer sem nenhum caso de teste.

Você pode notar que essa estrutura de eventos é inspirada na estrutura JUnit típica . Isso tem o propósito de manter as coisas próximas de algo básico sobre o qual os desenvolvedores geralmente têm conhecimento.

Relatórios de logs do executor de teste

Se você estiver escrevendo sua própria classe de teste ou executor Tradefed, você implementará IRemoteTest e obterá um ITestInvocationListener por meio do método run() . Este ouvinte pode ser usado para registrar arquivos da seguinte forma:

    listener.testLog(String dataName, LogDataType type_of_data, InputStreamSource data);

Testando com um dispositivo

A interface mínima acima permite executar testes muito simples que são isolados e não requerem nenhum recurso específico, por exemplo, testes de unidade Java.

Os redatores de teste que desejam ir para a próxima etapa do teste de dispositivos precisarão das seguintes interfaces:

  • IDeviceTest permite receber o objeto ITestDevice que representa o dispositivo em teste e fornece a API para interagir com ele.
  • IBuildReceiver permite que o teste obtenha o objeto IBuildInfo criado na etapa do provedor de compilação contendo todas as informações e artefatos relacionados à configuração do teste.

Os executores de teste geralmente estão interessados ​​nessas interfaces para obter artefatos relacionados à execução, por exemplo, arquivos extras, e obter o dispositivo em teste que será direcionado durante a execução.

Testando com vários dispositivos

Tradefed suporta a execução de testes em vários dispositivos ao mesmo tempo. Isso é útil ao testar componentes que exigem uma interação externa, como um telefone e um par de relógios.

Para escrever um executor de teste que possa usar vários dispositivos, você precisará implementar o IMultiDeviceTest , que permitirá receber um mapa de ITestDevice para IBuildInfo que contém a lista completa de representações de dispositivos e suas informações de compilação associadas.

O setter da interface sempre será chamado antes do método run , então é seguro assumir que a estrutura estará disponível quando run for chamado.

Testes cientes de suas configurações

NOTA: Este não é um caso de uso muito comum. Ele é documentado para ser completo, mas normalmente você não precisará dele.

Algumas implementações de executores de teste podem precisar de informações sobre a configuração geral para funcionar corretamente, por exemplo, alguns metadados sobre a invocação ou qual target_preparer executou antes, etc.

Para isso, um executor de teste pode acessar o objeto IConfiguration do qual faz parte e no qual é executado. Consulte a descrição do objeto de configuração para obter mais detalhes.

Para a implementação do executor de teste, você precisará implementar o IConfigurationReceiver para receber o objeto IConfiguration .

Executor de teste flexível

Os executores de teste podem fornecer uma maneira flexível de executar seus testes se tiverem um controle granular sobre eles, por exemplo, um executor de testes JUnit pode executar individualmente cada teste de unidade.

Isso permite que o conjunto e a infraestrutura maiores aproveitem esse controle preciso e os usuários executem parcialmente o executor de teste por meio de filtragem .

O suporte à filtragem está descrito na interface ITestFilterReceiver , que permite receber conjuntos de filtros de include e exclude para os testes que devem ou não ser executados.

Nossa convenção é que um teste será executado IFF se corresponder a um ou mais dos filtros de inclusão E não corresponder a nenhum dos filtros de exclusão. Se nenhum filtro de inclusão for fornecido, todos os testes deverão ser executados, desde que não correspondam a nenhum dos filtros de exclusão.

NOTA: Incentivamos os executores de teste a serem escritos de uma maneira que suporte essa filtragem, pois fornece um enorme valor agregado na infraestrutura maior. Mas entendemos que em alguns casos não é possível fazê-lo.