Análise e teste de código
Utilização correta de tipos de dados e conversões de tipo
Processamento de campos de caracteres
Uso de push-down de código no ABAP SQL
Melhorar o desempenho da tabela interna
Implementar verificações de autorização
Projetando código efetivo orientado a objetos
Definir e trabalhar com classes de exceção
Adicionar documentação à codificação ABAP

Implementação de testes de código com ABAP Unit

Objectives

After completing this lesson, you will be able to:
  • Implementar uma classe de teste.
  • Executar um teste de ABAP Unit.

ABAP Unit

Testes de módulo

Sempre que os programadores escrevem ou alteram o código, há uma chance justa de que eles introduzam erros de programação. Portanto, o teste é uma parte crucial de cada projeto de desenvolvimento.

Na programação moderna, o código é estruturado em classes e métodos reutilizáveis. Para detectar e localizar potenciais erros de programação, é necessário um teste completo de cada unidade de modularização.

Vejamos um exemplo.

ABAP Unit - Implementar testes de módulo em ABAP

O ABAP Unit é uma técnica que permite a você implementar testes de módulo com a linguagem ABAP. Você pode acionar a execução de teste manualmente durante o desenvolvimento, mas também automaticamente em uma escala maior e regularmente.

Os testes ABAP Unit são implementados como métodos de classes ABAP designadas especialmente. Esses métodos de teste servem como scripts de teste, com os quais o código em teste pode ser executado e com o qual os resultados podem ser avaliados.

Vamos continuar com nosso exemplo.

É muito importante que, uma vez que os testes estejam lá, você pode executá-los a qualquer momento e quantas vezes quiser: Antes do envio inicial, depois de ter aplicado modificações ao código produtivo, ou para análise de erros quando um usuário relata um problema. E a execução dos testes custa apenas alguns cliques do mouse.

Funcionalidades importantes da unidade ABAP

Classes de teste de unidade

A adição, FOR TESTING, na definição de classe distingue uma classe de teste de uma classe ABAP normal. A adição está disponível para classes locais, bem como para classes globais.

A adição, NÍVEL DE RISCO, é utilizada para atribuir um nível de risco ao teste. Se a adição estiver ausente, o nível de risco CRÍTICO será usado por padrão.

As configurações centrais no nível do mandante podem não permitir a execução de testes com determinado nível de risco.

Estão disponíveis os seguintes valores de nível de risco:

CRÍTICO

Um teste modifica as configurações do sistema ou os dados do customizing

PERIGOSO

Um teste modifica dados persistentes

HARMLESS

Um teste não modifica as configurações do sistema nem os dados persistentes

A adição DURATION indica o tempo de execução previsto. As configurações centrais no nível do mandante podem ser usadas para definir limites superiores do tempo de execução para os três valores.

Estão disponíveis os seguintes valores de duração:

CURTO

Espera-se um tempo de execução imperceptível de alguns segundos.

MÉDIA

Espera-se um tempo de execução perceptível de cerca de um minuto.

LONGO

Espera-se um tempo de execução muito perceptível de mais de um minuto.

Uma classe de teste pode ter dois tipos de métodos:

Métodos de teste

Os métodos de teste são definidos com o suplemento FOR TESTING após o nome do método. Cada método de teste representa um teste. O framework de ABAP Unit executa este teste chamando o método de teste relacionado. Os métodos de teste não podem ter parâmetros.

Métodos auxiliares

Os métodos auxiliares são métodos comuns da classe de teste. Eles não são chamados pelo framework de ABAP Unit. Você pode utilizar métodos auxiliares para estruturar o código de seus métodos de teste ou se quiser reutilizar a mesma funcionalidade em vários métodos de teste. Os métodos auxiliares podem ter qualquer número de parâmetros.

Nota

O framework de ABAP Unit pode chamar todos os métodos de teste, mesmo que sua visibilidade esteja definida como PRIVATE. Na verdade, é recomendável definir métodos de teste na seção privada da classe de teste para garantir que as classes de teste não sejam chamadas diretamente em nenhum lugar do código.

Uma classe de teste deve conter pelo menos um método de teste. Um método de teste não deve ter parâmetros.

Ao executar os testes de uma classe de teste, o framework de ABAP Unit chama todos os testes na classe de teste em uma ordem indeterminada.

Se você definir classes de teste locais dentro de uma classe ABAP global, existe um local dedicado para isso. Enquanto as classes locais normais são definidas na guia Tipos locais, as classes de teste locais devem ser definidas na guia Classes de teste.

Como ilustrado na figura, para gerar a parte de definição e a parte de implementação de uma classe de teste local, a ADT oferece a classe de teste de modelo de código.

Na demonstração Como definir e implementar uma classe de teste, você verá como chamar este modelo de código.

Implementação de teste de unidade

Classe de serviço CL_ABAP_UNIT_ASSERT

Em geral, a implementação de um método de teste tem a seguinte estrutura:

  1. Executar a codificação produtiva em teste
  2. Analisar o resultado
  3. Notificar resultado inesperado ao framework ABAP Unit

Para a etapa 3, o framework de ABAP Unit fornece a classe de serviço global CL_ABAP_UNIT_ASSERT. Os métodos de teste chamam os métodos estáticos desta classe para notificar erros e influenciar a execução de teste (por exemplo, ignorar um ou vários testes porque os pré-requisitos não foram cumpridos).

  • O método fail( ) relata um erro incondicional. Normalmente, uma chamada deste método é circundada por uma estrutura de controle como IF … ENDIF. ou TRY … ENDTRY para garantir que só é alcançada sob uma condição.
  • Métodos que começam com assert_ verificam uma determinada expectativa e relatam um erro se essa expectativa não for atendida.
    • O método assert_equals( ), por exemplo, compara o conteúdo de dois objetos de dados e notifica um erro se eles forem diferentes.
    • O método assert_diff( ) faz o mesmo, mas notifica um erro se os objetos de dados tiverem o mesmo conteúdo.

Parâmetros do método falham( )

Assista ao vídeo a seguir para saber mais.

Parâmetros de métodos de asserção

Todos os métodos de asserção têm parâmetros de importação opcionais MSG, LEVEL e QUIT, que sempre têm o mesmo significado que no método fail( ).

Além disso, a maioria dos métodos de asserção tem um parâmetro de importação ACT para o objeto de dados a ser verificado.

Comparando métodos, como assert_equals( ), assert_diff( ), e assim por diante, também tem um parâmetro EXP para o valor esperado.

Como definir e implementar uma classe de teste

Nesta demonstração, você verá como criar e implementar uma classe de teste local.

Execução de teste de unidade

Existem duas formas de executar testes ABAP Unit: testes interativos durante o desenvolvimento e testes em massa com ABAP Test Cockpit.

Assista ao vídeo a seguir para saber mais sobre a execução de teste de unidade.

Resultados de teste de ABAP Unit

No ADT, o resultado do teste ABAP Unit mais recente é exibido na visão de ABAP Unit.

O resumo na parte superior exibe o número total de métodos de teste que foram executados e a duração total do teste em milissegundos.

Utilize os campos de seleção no resumo para filtrar a exibição do resultado: somente testes que falharam, somente testes com advertências, somente testes que terminaram com êxito, etc.

O resultado em si é exibido como uma árvore com os objetos do repositório no topo, por exemplo, a classe global. Os nós no segundo nível representam as classes de teste locais e os pontos finais correspondem aos métodos de teste. Os ícones ajudam você a distinguir entre testes com êxito, falhas, etc.

Um clique com o botão esquerdo seleciona o método de teste e exibe detalhes no lado direito. No exemplo, você pode ver o nível de gravidade (Erro de asserção crítico para o meio de gravidade), o valor do parâmetro MSG ('Nenhuma exceção') e o valor do parâmetro DETALHES (o texto expandido abaixo de Detalhes).

Como executar um teste de módulo e analisar o resultado

Nesta demonstração, você verá como executar os testes de unidade e analisar os resultados.

Testar expositores e pré-requisitos

Métodos para expositores de teste

Às vezes, um teste precisa de uma determinada configuração antes de poder ser executado corretamente. Uma configuração de teste desse tipo é referida como expositor de teste. Um expositor de teste pode consistir em dados de teste, objetos de teste e recursos.

Para criar e remover expositores, você pode implementar métodos adicionais em uma classe de teste. Esses métodos têm nomes predefinidos e são chamados automaticamente pelo ambiente de tempo de execução ABAP quando o teste é executado.

Existem os seguintes métodos de expositor:

  • CONFIGURAÇÃO

    Este método de instância é chamado antes de cada teste da classe de teste. Utilize este método para expositores que você quer criar novamente para cada teste.

  • TEARDOWN

    Este método de instância é chamado após cada teste da classe de teste. Utilize este método para anular as modificações que você efetuou no método SETUP. A utilização de TEARDOWN é particularmente importante se SETUP efetuar modificações em dados persistentes (configuração do sistema, customizing, dados mestre, etc.).

  • CLASS_SETUP

    Este método estático é executado uma vez antes do primeiro teste da classe de teste. Utilize este método somente para expositores demorados e para os quais você tem a certeza de que as configurações não são modificadas por nenhum dos métodos de teste.

  • CLASS_TEARDOWN

    Este método estático é executado uma vez após o último teste da classe de teste. Utilize este método para anular as modificações que você efetuou no método CLASS_SETUP.

Lógica de processamento de teste de unidade ABAP

Este gráfico ilustra a execução do programa de ABAP UNIT para uma única classe de teste.

  • Primeiro, a classe de teste é carregada para a memória do programa. Se a classe de teste contiver um construtor estático (método estático CLASS_CONSTRUCTOR), este método é executado como habitualmente.
  • Em seguida, são realizados os testes desta classe de teste. Cada teste começa com a criação de uma instância da classe de teste. Se a classe de teste contiver um construtor de instância (método CONSTRUCTOR), ela é executada como habitualmente.
  • Em seguida, o framework de ABAP Unit chama o método de teste.
  • Após o teste, a instância é descartada.

Se existir mais de um método de teste na classe de teste, é criada uma nova instância para cada teste.

Após o último método de teste, o processamento desta classe de teste é concluído e o framework continua com a próxima classe de teste, se existir.

Modelo de fase do teste de unidade ABAP

Este gráfico ilustra o modelo de fases para testes ABAP Unit, ou seja, os momentos em que o framework chama métodos de expositor.

CLASS_SETUP só é chamado uma vez, após o construtor estático e antes do primeiro teste.

CLASS_TEARDOWN é chamado antes do framework iniciar o processamento da próxima classe de teste.

SETUP é chamado após o construtor de instância e antes da execução de teste.

TEARDOWN é chamado antes de o framework rejeitar a instância de classe de teste.

Pré-requisitos de relatórios ausentes

Você já aprendeu a utilizar métodos da classe de serviço CL_ABAP_UNIT_ASSERT para notificar testes falhados.

Mas o que você deve fazer no caso de um pré-requisito em falta, por exemplo, autorizações em falta? Ou uma situação em que um método SETUP tem problemas na preparação do teste?

Exatamente para essas situações, a classe CL_ABAP_UNIT_ASSERT contém um método skip( ) e vários métodos que começam com ASSUME_.

Os métodos funcionam de forma bastante semelhante a fail( ) e aos métodos assert, mas eles parecem diferentes no resultado do teste UNIT. Isso facilita a distinção entre erros na codificação testada e erros na configuração do sistema ou no design de teste.

Vejamos um exemplo:

O método test_with_fail( ) chama o método cl_abap_unit_assert=>fail( ). Este método é contado como um teste falhado e o Rastreamento de falha para este método exibe Erro de asserção crítica na frente do texto da mensagem.

O método test_with_skip( ) chama o método cl_abap_unit_assert=>skip( ). Este método é contado como um teste cancelado e o Rastreamento de falhas para este método exibe Pré-requisitos em falta na frente do texto da mensagem.

Nota

Não existem parâmetros QUIT e LEVEL nos métodos skip( ) e assume-method. Se faltarem pré-requisitos, não faz sentido continuar o teste e não distinguimos entre diferentes gravidades.

Como executar um teste de módulo complexo com SETUP

Nesta demonstração, você verá como executar um teste mais complexo que contém um método de configuração.

Implementar e executar um teste de ABAP Unit

Você percebe que algo não está certo com a saída do seu código. Em particular, a data para o próximo voo de carga disponível parece estar incorreta. Você define e implementa um teste de ABAP Unit para o método find_cargo_flight da classe local lcl_carrier para analisar mais detalhadamente este problema.

Modelo:

  • /LRN/CL_S4D401_ATS_CHECKED (classe global)

Solução:

  • /LRN/CL_S4D401_ATS_UNIT_TEST (classe global)

Tarefa 1: Copiar modelo (opcional)

Copie a classe modelo /LRN/CL_S4D401_ATS_CHECKED. Se você tiver concluído o exercício anterior, pode ignorar esta tarefa e continuar processando sua classe ZCL_##_SOLUTION.

Etapas

  1. Copie a classe /LRN/CL_S4D401_ATS_CHECKED para uma classe em seu próprio pacote (nome sugerido: ZCL_##_SOLUTION, onde ## representa seu número de grupo).

    1. No Explorador de projetos, clique com o botão direito na classe /LRN/CL_S4D401_ATS_CHECKED para abrir o menu de contexto.

    2. No menu de contexto, selecione Duplicar....

    3. Insira o nome do seu pacote no campo Pacote. No campo Nome, insira o nome ZCL_##_SOLUTION, onde ## representa seu número de grupo.

    4. Selecione Avançar.

    5. Confirme a ordem de transporte e selecione Concluir.

  2. Ative a cópia.

    1. Pressione Ctrl + F3 para ativar a classe.

Tarefa 2: Definir classe de teste local

Em sua classe global, defina uma classe de teste local (nome proposto: ltcl_find_flights). Defina e implemente um teste para o método find_cargo_flight da classe lcl_carrier.

Etapas

  1. Crie uma classe de teste local ltcl_find_flights.

    1. Navegue para a ficha de registro Classes de teste (não existentes).

    2. Se um botão Criar classes de teste... for exibido na ficha de registro, selecione o mesmo.

    3. Posicione o cursor no fim do comentário na primeira linha do texto fonte e pressione Enter.

    4. Na nova linha de texto fonte, digite test e pressione Ctrl + Space para chamar o code completion.

    5. Selecione o modelo de texto fonte testClass - Test class (ABAP Unit) e pressione Enter.

    6. Enquanto o nome da classe preliminar ltcl_ estiver destacado, digite o nome completo da classe ltcl_find_flights.

  2. Use uma correção rápida para renomear o método de teste predefinido (nome sugerido: test_find_cargo_flight).

    1. Clique com o botão direito do mouse em first_test e selecione Correção rápida. Na lista exibida, selecione Renomear first_test. Enquanto o nome estiver destacado, digite o novo nome test_find_cargo_flight.

  3. Implemente o método test_find_cargo_flight. Leia os campos-chave (carrier_id, connection_id, flight_date) e os dois aeroportos (airport_from_id e airport_to_id) de qualquer voo de carga com uma capacidade livre de pelo menos 1 kg da tabela de banco de dados /LRN/CARGOFLIGHT. Armazene o resultado em um objeto de dados adequado (nome sugerido: some_flight_data).

    Dica

    A capacidade livre é calculada como a diferença entre os dois campos de tabela maximum_load e actual_load.
    1. Ajuste o código da seguinte forma:

      ABAP
      12345678910
      METHOD test_find_cargo_flight. SELECT SINGLE FROM /lrn/cargoflight FIELDS carrier_id, connection_id, flight_date, airport_from_id, airport_to_id WHERE maximum_load - actual_load >= 1 INTO @DATA(some_flight_data). ENDMETHOD.
  4. Se não existir nenhum conjunto de dados adequado na tabela /LRN/CARGOFLIGHT, notifique o teste como falhado.

    1. Ajuste o código da seguinte forma:

      ABAP
      1234567891011121314
      METHOD test_find_cargo_flight. SELECT SINGLE FROM /lrn/cargoflight FIELDS carrier_id, connection_id, flight_date, airport_from_id, airport_to_id WHERE maximum_load - actual_load >= 1 INTO @DATA(some_flight_data). IF sy-subrc <> 0. cl_abap_unit_assert=>fail( `No suitable data in table /LRN/CARGOFLIGHT` ). ENDIF. ENDMETHOD.
  5. Se você tiver conseguido ler um registro adequado da tabela /LRN/CARGOFLIGHT, utilize o valor carrier_id deste registro para criar uma instância da classe lcl_carrier (nome sugerido para a referência: the_carrier).

    1. Adicione o seguinte código no final do método:

      ABAP
      12
      DATA(the_carrier) = NEW lcl_carrier( i_carrier_id = some_flight_data-carrier_id ).
  6. Se o construtor da classe lcl_carrier criar um relatório de exceção, o teste como falhou.

    1. Ajuste o código da seguinte forma:

      ABAP
      123456
      TRY. DATA(the_carrier) = NEW lcl_carrier( i_carrier_id = some_flight_data-carrier_id ). CATCH cx_abap_invalid_value. cl_abap_unit_assert=>fail( `Unable to instantiate lcl_carrier` ). ENDTRY.
  7. Se a instanciação tiver sido efetuada com êxito, chame o método find_cargo_flight para a instância de lcl_carrier. Utilize os aeroportos e a data do voo de carga selecionado como entrada. Defina a carga livre mínima (parâmetro i_load) em 1. Armazene o resultado em objetos de dados adequados (nomes sugeridos: voo e days_later).

    1. Adicione o seguinte código no final do método:

      ABAP
      12345678910
      the_carrier->find_cargo_flight( EXPORTING i_airport_from_id = some_flight_data-airport_from_id i_airport_to_id = some_flight_data-airport_to_id i_from_date = some_flight_data-flight_date i_cargo = 1 IMPORTING e_flight = data(flight) e_days_later = data(days_later) ).
  8. Analise a saída do método. Chame métodos assert adequados para garantir que o parâmetro e_flight retorne uma referência de objeto válida e que o parâmetro e_days_later retorne zero.

    Nota

    O fato de o parâmetro e_days_later retornar o valor zero significa que o método encontrou um voo adequado diretamente no dia solicitado. Isso é o que esperamos se usarmos as propriedades de um voo existente como entrada.
    1. No final do método, adicione o seguinte código:

      ABAP
      12345678910
      cl_abap_unit_assert=>assert_bound( act = flight msg = `Method find_cargo_flight does not return a result` ). cl_abap_unit_assert=>assert_equals( act = days_later exp = 0 msg = `Method find_cargo_flight returns wrong result` ).
  9. Ative sua classe.

    1. Pressione Ctrl + F3 para ativar a classe.

Tarefa 3: Executar teste de unidade

Execute o teste de módulo. Analise o resultado e ajuste a implementação do método find_cargo_flight se o teste retornar um erro.

Cuidado

Se o teste de módulo for cancelado com a mensagem Duração máxima permitida de 60 segundos excedida, aumente o tempo de execução previsto. Para isso, modifique o suplemento DURATION na instrução CLASS … DEFINITION de sua classe de teste de DURATION SHORT para DURATION MEDIUM.

Etapas

  1. Execute os testes de módulo em sua classe global ZCL_##_SOLUTION.

    1. No Explorador de projetos, clique com o botão direito do mouse na classe ZCL_##_SOLUTION e selecione Executar comoteste de unidade ABAP.

  2. Quando o teste for concluído, analise o resultado na visão de ABAP Unit.

    1. Se ainda não for exibido, clique na guia ABAP Unit na seção inferior da tela.

    2. Você deve ver o valor 1 ao lado do ícone Falhas/erros.

    3. Na árvore, selecione o nome do método de teste ( zcl_##_solutionltcl_find_flightstest_find_cargo_flight ) para exibir os detalhes do erro.

  3. Analise o código do método find_cargo_flight e corrija o erro.

    1. Na árvore, clique duas vezes em test_find_cargo_flight para navegar para a implementação do método de teste.

    2. Role para baixo até a chamada do método find_cargo_flight. Posicione o cursor no nome do método e pressione F3 para navegar para a respectiva implementação.

    3. Encontre o cálculo de days_later. Aqui, a data do voo é subtraída da data fornecida. Este deve ser o contrário.

    4. Para corrigir o erro, adicione um sinal de comentário na frente desta instrução:

      ABAP
      1
      * DATA(days_later) = i_from_date - flight->flight_date.

      E substitua-o por este código:

      ABAP
      1
      DATA(days_later) = flight->flight_date - i_from_date.
  4. Ative sua classe e execute novamente o teste de módulo para confirmar que o erro desapareceu agora.

    1. Pressione Ctrl + F3 para ativar a classe.

    2. Selecione Repetir testes na barra de ferramentas da guia ABAP Unit.

    3. Certifique-se de que o teste agora mostra zero falhas/erros.

  5. Execute sua classe global como aplicativo de console para garantir que a data do próximo voo de carga disponível esteja razoavelmente próxima no futuro.

Tarefa 4: Implementar o método CLASS_SETUP (opcional)

Você planeja adicionar mais métodos de teste à sua classe de teste, que todos precisarão de uma instância de lcl_carrier. Para melhorar a performance de seus testes, você desloca a instanciação demorada da classe lcl_carrier para o método class_setup, uma vez que este método é executado somente uma vez para todos os testes da classe de teste.

Etapas

  1. Defina um método estático privado class_setup (sem parâmetros) na sua classe de teste ltcl_find_flights e utilize uma solução rápida para adicionar a implementação do método.

    1. Adicione o seguinte código à seção privada da classe ltcl_find_flights.

      ABAP
      1
      CLASS-METHODS class_setup.
    2. Clique com o botão direito do mouse em class_setup e selecione Correção rápida.

    3. Na lista de sugestões, selecione Adicionar implementação para class_setup.

  2. Na classe de teste, declare um atributo estático privado no qual você pode armazenar uma referência a uma instância da classe lcl_carrier (nome proposto: the_carrier).

    1. Adicione o seguinte código à seção privada da classe ltcl_find_flights.

      ABAP
      1
      CLASS-DATA the_carrier TYPE REF TO lcl_carrier.
  3. Declare um atributo estático privado no qual você pode armazenar um registro da tabela de banco de dados /LRN/CARGOFLIGHT (nome sugerido: some_flight_data).

    1. Adicione o seguinte código à seção privada da classe ltcl_find_flights.

      ABAP
      1
      CLASS-DATA some_flight_data TYPE /lrn/cargoflight.
  4. Desloque a instrução SELECT e a instanciação da classe lcl_carrier do método test_find_cargo_flight para o método class_setup. Arquive os dados do banco de dados no atributo estático some_flight_data e a referência à instância de lcl_carrier no atributo estático the_carrier.

    1. Corte o seguinte código da implementação do método test_find_cargo_flight e cole-o na implementação do método class_setup.

      ABAP
      1234567891011121314151617
      SELECT SINGLE FROM /lrn/cargoflight FIELDS carrier_id, connection_id, flight_date, airport_from_id, airport_to_id WHERE maximum_load - actual_load >= 1 INTO @DATA(some_flight_data). IF sy-subrc <> 0. cl_abap_unit_assert=>fail( `No suitable data in table /LRN/CARGOFLIGHT` ). ENDIF. TRY. DATA(the_carrier) = NEW lcl_carrier( i_carrier_id = some_flight_data-carrier_id ). CATCH cx_abap_invalid_value. cl_abap_unit_assert=>fail( `Unable to instantiate lcl_carrier` ). ENDTRY.
    2. Substitua. INTO @DATA(some_flight_data) com INTO CORRESPONDING FIELDS OF @some_flight_data

    3. Substitua. DATA(the_carrier) com the_carrier

    4. Agora, o código do método class_setup deve ter o seguinte aspecto:

      ABAP
      123456789101112131415161718192021
      METHOD class_setup. SELECT SINGLE FROM /lrn/cargoflight FIELDS carrier_id, connection_id, flight_date, airport_from_id, airport_to_id WHERE maximum_load - actual_load >= 1 INTO CORRESPONDING FIELDS OF @some_flight_data. IF sy-subrc <> 0. cl_abap_unit_assert=>fail( `No suitable data in table /LRN/CARGOFLIGHT` ). ENDIF. TRY. the_carrier = NEW lcl_carrier( i_carrier_id = some_flight_data-carrier_id ). CATCH cx_abap_invalid_value. cl_abap_unit_assert=>fail( `Unable to instantiate lcl_carrier` ). ENDTRY. ENDMETHOD.
  5. Ative a classe e execute o teste de módulo novamente para garantir que ela ainda funcione.

    1. Pressione Ctrl + F3 para ativar a classe.

    2. Execute o teste de unidade como antes.