Prévia do material em texto
TESTES DE APLICATIVOS MÓVEIS AULA 3 Prof. Felipe Pedroti Raymundo 2 CONVERSA INICIAL Até agora tivemos contato com uma parte superficial do ponto de vista dos testes, principalmente para podermos ter uma base teórica boa e forte, para que os próximos conceitos possam ser aplicados de maneira fluida e simples. Vimos que os testes existem das mais diversas maneiras e de vários tipos, cada um deles com sua devida importância dentro de um processo de desenvolvimento, independentemente do tipo de processo. Vimos também que testes podem ser aplicados não somente ao software propriamente dito, mas também às fases de design e construção conceitual do projeto, validando as informações levantadas pela equipe de análise. Também vimos quem juntamente aos tipos e processos de testes existentes, podemos utilizar algumas ferramentas, os frameworks para que eles nos auxiliem nessa tarefa. Nesta etapa, vamos aprofundar um pouco mais no que, ou melhor dizendo, o quanto podemos fazer utilizando esses frameworks, para iniciarmos a aplicá-los em nossas soluções. Crédito: Oleksiy Mark/Shutterstock. 3 TEMA 1 – VOLTANDO AOS TESTES UNITÁRIOS Se puxarmos na memória, vimos que existem várias fases de testes, com diversos tipos. Voltando aos processos de software, podemos notar que a estruturação dos nossos projetos em camadas é uma das soluções mais adotadas hoje, seja para diminuir o acoplamento entre artefatos de software, seja para facilitar a manutenção de uma interface de usuário, por exemplo, sem que esta afete diretamente as regras de negócio do software. Essa abordagem também vai facilitar (e muito), a vida do projetista de testes, principalmente porque ele vai poder escrever testes unitários para essas camadas, podendo, na maioria dos casos, automatizar os processos de publicação do software, passando por rotinas de testes para garantia da qualidade, o que deve ser muito reforçado nos times de desenvolvimento, apesar da negligência destes na maioria dos casos. Reforçando o conceito: testes unitários são pequenos trechos de código para testar o comportamento de outros trechos de código e estão dentro do nível de teste mais inferior, sendo um dos testes menos custosos para todo o projeto. Devem ser utilizados para verificar situações em que temos que validar, por exemplo, lógica, operações e resultados. Nabil (2022, tradução nossa) afirma que “muitos desenvolvedores subestimam a importância de escrever testes unitários”. Vamos observar algumas vantagens de utilizar testes unitários: 1. Reduz o custo efetivo do teste, pois os defeitos já são identificados em fases iniciais do projeto (principalmente quando o conceito de TDD é aplicado); 2. Gera de certa forma uma documentação do software, pois por eles temos uma visão geral do funcionamento de tudo; 3. Reduz a complexidade de código (aqui o TDD é bem mais visível e aplicado, principalmente na sua fase 3 onde refatoramos o código-fonte escrito); 4. Reduz a quantidade de bugs as realizar mudanças no código-fonte, pois se o teste estava passando, e agora não está o desenvolvedor tem um norte por onde começar. 4 5. Melhora não somente a estrutura, mas também permite que fique um código-fonte mais limpo devido a refatoração (novamente aplicando aqui o conceito da fase 3 do TDD). Um outro conceito que conseguimos obter (e como um próximo passo obter leituras e relatórios) é o da cobertura de código. Com testes unitários, podemos obter uma noção de quanto do nosso código temos testados e conseguimos saber onde e o que está ficando de fora dos testes. Analisando tudo isso, o que nos faz criar testes unitários eficientes? Ou o que determina que um teste unitário é bom ou ruim? Nabil (2022) enumera quatro pontos de atenção, sobre o que faz um teste unitário bom ou ruim: 1. Testes devem ser executados de forma rápida; 2. Testes devem ser totalmente automatizados, e a saída deve ser sempre binária, passou ou não passou; 3. Testes unitários são unitários, não devem compartilhar estado nem informações com outros testes, ou seja, independentes e isolados; 4. Devemos usar sempre TDD, escrevendo os testes antes de escrever código de produção. Figura 1 – Fluxo das fases do TDD 5 TEMA 2 – TESTES UNITÁRIOS EM APLICATIVOS HÍBRIDOS – XAMARIN Relembrando o que vimos no módulo de desenvolvimento híbrido, o Xamarin é um dos frameworks para desenvolvimento híbrido disponível no mercado. Ele vem de forma nativa durante a instalação do Visual Studio, e com ele podemos escrever um único código em que, independentemente do ambiente em que rode, o mesmo software com a mesma lógica será executado, e na maioria dos casos com a mesma interface. Um dos paradigmas mais fortes no desenvolvimento híbrido com Xamarin é a separação entre as camadas, utilizando um padrão de projeto conhecido como Model-View-ViewModel, ou MVVM. Esse conceito cai como uma luva para os times de teste, pois este é um padrão de projeto que visa separar a camada lógica da aplicação, da camada de apresentação, diminuindo o acoplamento entre elas. Vamos imaginar que temos uma base de código compartilhada, e precisamos criar testes para validar que a parte lógica do modelo esteja funcional. Em conjunto com o .NET, a Microsoft disponibilizou um framework para criação e escrita de testes de software, chamado de MSTest. Vamos observar a imagem a seguir para obter um norte sobre como está nossa base de código compartilhada. Neste exemplo usaremos um projeto do tipo class library, baseado no .NET Framework 4.8, conforme Figura 2. 6 Figura 2 – Classe calculadora e projeto A intenção aqui é saber como podemos testar se essas operações estão corretas, e fazendo o que é preciso fazer de verdade (somar e diminuir). Para isso, vamos adicionar um novo projeto a nossa solução de exemplo, do tipo Unit Test Project (.NET Framework). Ao adicionar o projeto, vamos ver que ele já vai ser criado com uma classe, contendo algumas instruções e atributos que vamos ver abaixo. O atributo (TestClass) fala para o framework que essa classe contém testes unitários. A anotação (TestMethod) indica que o método abaixo a ela é um método que será considerado como um teste. Para continuarmos, vamos adicionar o nosso projeto como referência ao projeto de teste e implementar de forma propriamente dita o nosso teste: 7 Figura 3 – Teste implementado Ao clicarmos com o botão direito do mouse sobre o nome do método de teste, podemos ver que uma opção vai surgir chamada Run Test. Ao selecionar essa opção, vamos ver que uma janela chamada Test Explorer surgiu na IDE, onde ela vai listar a nossa classe de teste e o método que executamos. Entretanto, vamos notar que o teste inicialmente não passou, e a mensagem mostrou que os resultados não são iguais. Isso ocorreu porque, intencionalmente, fizemos uma operação de soma de dois valores, porém o método está multiplicando dois valores. Vamos refatorar o código, e realizar a correção para ver o que o Test Explorer vai nos mostrar: Figura 4 – Janela de resultados do Test Explorer 8 Figura 5 – Método corrigido Figura 6 – Test explorer mostrando sucesso no teste Parece muito simples escrever testes unitários, porém, isso dá um poder para a equipe de desenvolvimento muito grande, principalmente quando estamos falando de muitas pessoas trabalhando num mesmo projeto. Neste exemplo, observamos uma instrução que vai garantir que os princípios para um bom teste unitário sejam garantidos. Ele executou rapidamente, testou um único pedaço de código sem compartilhar estado (isolado), deu um resultado único e booleano e passou (ou não passou, como na primeira execução). A garantia de automatizaçãodos testes unitários criados com o MSTest é dada pelas classes do tipo Assert. Entre alguns dos métodos mais comuns dessa classe, podemos citar os seguintes: • AreEqual e suas sobrecargas – método que compara tipos garantindo que sejam iguais; • AreNotEqual e suas sobrecargas – método que ao contrário do AreEqual, valida se os tipos comparados são diferentes; • AreSame e AreNotSame – métodos que comparam objetos e se são os mesmos; • Fail – método que lança uma exceção do tipo AssertFailedException; • IsFalse e IsTrue – métodos que comparam uma condição booleana; • IsNull e IsNotNull – métodos que validam se o objeto especificado é nulo ou não nulo. 9 É possível utilizarmos esse mesmo conceito para testar todas as camadas de um aplicativo Xamarin utilizando MVVM. Vale lembrar que a arquitetura facilita a aplicação do conceito, mas não é uma obrigatoriedade para que utilizemos testes no projeto. Figura 7 – Test explorer mostrando todos os testes na classe CalculadoraTest TEMA 3 – TESTES UNITÁRIOS EM APLICATIVOS HÍBRIDOS – OUTRAS PLATAFORMAS Sabemos que o Xamarin não é a única tecnologia para desenvolvimento de aplicativos híbridos disponível hoje no mercado. Muitas outras tecnologias, como o Flutter e o React, fazem esse trabalho com excelência também. Aqui fica a dúvida: existe a possibilidade de testar esses aplicativos com testes unitários? E a resposta é simples: sim. 3.1 Flutter Vamos, inicialmente, abordar o Flutter. Assim como o Xamarin, no Flutter é possível criar classes e bibliotecas que podem ser reaproveitadas, e essas bibliotecas podem ser testadas sem problema nenhum. O Flutter, assim como o .NET, tem um pacote que permite que escrevamos testes unitários para nossa biblioteca, assim como o pacote flutter_test permite que escrevamos testes que validem o comportamento dos widgets. Vamos exemplificar a criação de testes em Flutter (o objetivo aqui é mostrar os recursos da plataforma para execução e criação de testes, não fazer uma visão geral de toda a plataforma e de como programar em Flutter). Nas classes a seguir, replicamos a mesma lógica que fizemos e a mesma classe calculadora abordada no segundo tópico. 10 Figura 8 – Classe Flutter calculadora Figura 9 – Classe Flutter calculadora_test Nessa situação, criamos uma classe de teste em que pedimos para que o método expect faça a comparação entre os resultados e valide se são o mesmo ou não. No Flutter, para agruparmos testes unitários de uma classe em uma execução utilizamos a instrução group: 11 Figura 10 – Agrupando testes unitários em Flutter As asserções dos testes escritos em Flutter são feitas utilizando o “expect()”. Esse método em específico pode utilizar qualquer combinação do pacote matcher do Flutter, para criar combinações complexas de validações. 3.2 React Native Avançando nas tecnologias híbridas para a escrita de aplicativos, temos outro framework amplamente utilizado hoje para esse fim: o React. Testes em React são escritos utilizando uma biblioteca chamada Jest, que inclusive já vem habilitada quando criamos um projeto React Native seguindo o template padrão. Para criarmos um teste, vamos observar o exemplo a seguir, retirado da própria documentação do projeto. 12 Figura 11 – Unit test escrito em React com Jest. Reprodução A descrição do teste é data pela instrução it, e a string passada como primeiro parâmetro. Em seguida, passamos uma função, com as comparações que iremos fazer, em que a asserção do resultado vai ser feita pela função expect. A documentação recomenda que devemos sempre pensar em três pontos principais para escrever testes no React, também conhecido como AAA (Arrange, Act, Assert): 1. Dada uma pré-condição; 2. Quando a execução do que estamos testando esteja concluída; 3. Então teremos o resultado esperado. Para que possamos agrupar a execução de testes no React, utilizamos a função describe, para que todos os testes de uma dada funcionalidade sejam executados juntos. TEMA 4 – TESTES UNITÁRIOS EM APLICATIVOS NATIVOS – ANDROID Nem somente de tecnologias de desenvolvimento híbridas sobrevive o mercado de aplicativos. Ainda muitos aplicativos que utilizamos no dia a dia são escritos utilizando as linguagens nativas de desenvolvimento. Neste tópico, vamos falar um pouco sobre como podemos aplicar TDD e testes unitários em um projeto nativo Android. Vamos, primeiramente, olhar como é a estrutura dos testes dentro de um projeto nativo (lembrando novamente que o objetivo aqui é mostrar como aplicar testes unitários dentro dos projetos, não dar uma visão geral sobre a plataforma e como codificar para ela). Aqui, o framework que está sendo utilizado é o JUnit, que já vem padrão com o template. 13 Figura 12 – Estrutura inicial de um projeto Android Nos projetos Android, temos duas pastas importantes quando tratamos de testes: androidTest, diretório que vai armazenar os testes que serão executados em dispositivos, geralmente testes de integração, ou de fim a fim; e test, que vai conter os testes que executaremos no ambiente local de desenvolvimento, como os testes unitários. Se observarmos com atenção, perceberemos que o template padrão de aplicativos Android até cria um arquivo de exemplo mostrando como os testes devem ser criados e executados. 14 Figura 13 – Teste padrão criado no aplicativo nativo Android A documentação do Android fala que as recomendações em relação aos tipos de teste unitários essenciais para que as boas práticas de programação sejam preservadas são as seguintes: • Criar testes unitários para ViewModels ou classes presenters; • Criar testes unitários para a camada de dados, especialmente repositórios; • Criar testes para outras camadas que não dependam da plataforma, como a camada de Domínio; • Criar testes para classes utilitárias. Vamos avançar um pouco e replicar nossa lógica da calculadora, criando uma classe que vai conter os métodos, e depois vamos criar uma nova classe de teste dentro do pacote test. 15 Figura 14 – Classe calculadora em Java Feito isso, se clicarmos no nome da classe e utilizarmos a combinação no teclado Alt + Insert, vamos ver que uma das opções é que criemos os testes para a classe em questão. Selecionada a opção, abre uma janela em que podemos configurar essa classe e selecionar quais métodos vamos testar nela. Figura 15 – Toolbox para criação de testes 16 Figura 16 – Classe de teste pronta para ser codificada A anotação “@Test” sobre os métodos da classe indica cada um dos testes unitários que serão executados dentro do ambiente. Vamos avançar o código para a implementação dos testes. Neste exemplo, podemos observar que a instrução asserEquals do JUnit é a responsável por validar se os resultados estão condizentes. Aqui um ponto de atenção é que, diferentemente das outas linguagens, o JUnit tem um valor de delta, que precisamos informar para poder calcular com que precisão a função vai trabalhar para definir que os resultados são iguais. 17 Figura 17 – Calculadora Test.java Ao clicarmos com o botão direito na classe e selecionarmos para que a IDE execute a classe de teste, ela será compilada e uma janela de execução será exibida, mostrando o resultado de todos os testes da classe, como podemos ver na Figura 18. Figura 18 – Janela do Android Studio mostrando o resultado dos testes TEMA 5 – TESTES UNITÁRIOS EM APLICATIVOS NATIVOS – IOS Para finalizarmos esta etapa, vamos observar como podemos fazer testes unitários dentro de um projeto iOS nativo, escrito em Swift. Para isso, a própria linguagem possui um framework específico para esse fim, conhecido comoXCTest. Por meio dele podemos escrever não somente testes unitários, mas 18 também como escrever testes que possam interagir com a interface do usuário para validar fluxos e/ou interações. Para que possamos criar os testes utilizando XCTest, devemos definir uma classe que herda de XCTestCase. Dentro destas classes, devemos criar métodos que não tenham retorno, parâmetros e que devem começar com o prefixo test, desta forma sendo detectados automaticamente pelo framework XCTest. Vejamos um exemplo a seguir, reproduzido da própria documentação. Figura 19 – Testes unitários em aplicativos nativos Nesse exemplo, podemos verificar que temos uma classe TableValidationTests, subclasse de XCTestCase, como orienta a documentação. Feito isso, foi criado um teste chamado testEmptyTableRowAndColumnCount(), onde utilizamos duas chamadas de asserções XCTAssertEqual, em que passamos os dados e uma mensagem caso o teste tenha resultado negativo. Existem inúmeros métodos e formas de asserções implementadas na API do XCTest, em que podemos ter uma visão geral na documentação do projeto. Além disso, seguindo os parâmetros, podemos utilizar uma função da IDE Xcode chamada Test Navigator, além de poder gerar relatórios de integração e identificação dos testes. 19 Figura 20 – Test navigator. Reprodução FINALIZANDO Pudemos verificar o que são testes unitários, bem como por que são importantes dentro do processo de desenvolvimento. Também validamos as vantagens e desvantagens de termos testes unitários no processo. Pudemos entender também o que faz um teste unitário bom ou ruim, e também entender que, por meio do TDD, esse processo fica muito mais natural. No segundo tópico, pudemos verificar como podemos escrever testes em aplicativos híbridos escritos em Xamarin, através do Visual Studio e do framework MSTest, visto que o próprio padrão de projeto do Xamarin chamado MVVM facilita a aplicação de testes unitários. Depois, continuamos vendo os testes unitários em ambientes híbridos, apresentando como fazer testes no Flutter e no React, utilizando os frameworks nativos de cada plataforma sendo o flutter_test e o Jest. Realizamos, então, a aplicação de testes unitários nos projetos nativos, sendo que primeiro vimos como escrever testes nos projetos Android, com JUnit, framework que já vem habilitado no template padrão de projetos nativos Android. Por fim, vimos como escrever testes unitários para projetos iOS escritos em Swift, utilizando o XCTest, e como devemos padronizar os testes de acordo com o indicado pela documentação. Em outra oportunidade, vamos começar a conhecer outra forma de teste de aplicativos, de forma que consigamos testar a interface dos nossos aplicativos. 20 REFERÊNCIAS NABIL, M. What is unit testing in mobile development? Bitrise, 14 jan. 2022. Disponível em: <https://blog.bitrise.io/post/what-is-unit-testing-in-mobile- development>. Acesso em: 31 out. 2022. TESTING overview. React, S.d. Disponível em: <https://reactjs.org/docs/testing.html>. Acesso em: 31 out. 2022. UNIT TESTING C# with MSTest and .NET. Microsoft, 11 mar. 2022. Disponível em: <https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-with- mstest>. Acesso em: 31 out. 2022. WHAT TO TEST in Android. Developers, S.d. Disponível em: <https://developer.android.com/training/testing/fundamentals/what-to-test>. Acesso em: 31 out. 2022. XCTEST. Developer, S.d. Disponível em: <https://developer.apple.com/documentation/xctest>. Acesso em: 31 out. 2022. Conversa inicial FINALIZANDO REFERÊNCIAS