iOS Tests 101

Yasmin Benatti
iFood Tech
Published in
6 min readOct 17, 2019

Neste artigo você vai descobrir como preparar o seu código para testes no desenvolvimento de aplicativos iOS.

Há um tempo, rolou um Meetup da Movile em São Carlos em que tive a oportunidade de falar um pouquinho sobre testes no desenvolvimento de aplicativos iOS. Em vez de só compartilhar a apresentação, achei mais interessante escrever uma série de posts mais completos com as informações que apresentei, assim vocês não perdem as dicas ;)

Antes de começar, alguns links:

Preparados? Aqui vamos entrar mais em detalhes sobre testabilidade, como escrever seu código de forma que ele fique mais limpo, mais testável e mais fácil de dar manutenção.

Overview

Com certeza, você já passou por aquela situação em que disse: “na próxima nova feature eu vou implementar testes.” E quando você recebeu as especificações, o tempo era tão curto que você disse “ok… fica para depois”?

Eu sei como é, então sinta-se abraçado! Mas agora isso pode mudar! Pelo menos, depois deste post você vai poder pensar de uma nova forma e escrever seu código com um novo ponto de vista. Aumentar a testabilidade e manutenibilidade de um código é sempre importante, até porque esperamos que ele não fique sem cobertura de testes para sempre, né?

Alguns termos se repetem bastante e é bom nos acostumarmos com eles. Uma sigla muito utilizada no contexto de testes é sut, que significa system under test, geralmente a classe que iremos testar. Outra sigla que sempre vemos em artigos e publicações sobre testes é TDD, test driven development, mas não vamos falar disso por hoje. Fica pra um outro artigo!

Durante o desenvolvimento existem algumas “etapas” que são comumente seguidas para compor e padronizar os testes:

  • Given
  • When
  • Then

Given

São os dados necessários para executar o trecho de código sob teste. Em um método de login, por exemplo, os dados necessários são o e-mail e a senha do usuário. Esses dados são mockados de forma que seja possível prever o resultado esperado na saída do método.

When

É de fato o método que vai ser testado, utilizando os dados mockados na etapa given.

Then

Após criar os dados de entrada e invocar o método, a saída criada pelo último é testada, através de expects ou asserts.

Além disso, todos os testes devem começar com a palavra test, pois somente métodos com esse prefixo são invocados durante a execução dos testes. Lembrando também que a ordem de execução das classes de teste é aleatória, assim como a de seus métodos.

Tipos de Testes

Agora vamos falar sobre três tipos principais de testes, o que cada um deve fazer e quais as suas vantagens. São eles:

  • Unit Tests
  • Integration Tests
  • UI Tests

Unit Tests

Uma unidade é a menor parte testável de código, sendo na prática uma função, um algoritmo, um pequeno pedaço independente que realiza uma atividade específica. Por exemplo, uma função joinNamesAndCapitalizeIt que recebe duas strings firstName e lastName, junta as duas e coloca em caixa alta as primeiras letras de cada palavra.

Para implementar o teste unitário desse método, seguimos aquela receitinha “given/when/then”:

Simples, não? Esse é o tipo de teste que deve ser feito por ser rápido, não ter interdependência com outras classes e métodos e porque garante o funcionamento de cada pequena parte do projeto.

Integration Tests

Bacana! Temos testes unitários implementados, garantimos que os pequenos pedacinhos de código funcionam isoladamente, mas e se a gente colocar tudo junto? É aí que entram os testes de integração, para garantir que partes independentes trabalhem corretamente em conjunto.

Esse tipo de teste é um pouco mais lento que os testes unitários, mas ainda podem ser considerados rápidos, pois não há a necessidade de subir a aplicação para executá-los. Vou deixar o exemplo desse tipo de teste para o post de exemplo prático, quando falarmos de arquitetura, então guarda essa informação aí no cantinho que voltaremos a isso num futuro não muito distante.

UI Tests

Também chamados de end-to-end tests, são os mais famosos por não dependerem de arquitetura. Mesmo que seu código esteja uma bagunça, é só rodar a aplicação e testar!

PERFEITO! Testo as chamadas de API, os métodos de login, os cliques de botão, as formatações de informação apresentada na tela… não poderia ter algo melhor, certo?! Até que … BUG! O que aconteceu? De onde vem esse bug? O que o causou? Tá no server? No ViewController? No CoreData?

A questão é: testes de interface são bons? São, mas devem ser utilizados com mais cautela. Esses testes são mais custosos, necessitam de uma aplicação rodando e, caso seu layout mude com muita frequência, é possível que eles mais quebrem do que garantam o funcionamento do seu app.

O Xcode tem uma ferramenta de UI test bem interessante, em que você inicia uma gravação do simulador, clica nos botões e campos e transforma isso em código, automatizando o processo nas próximas execuções. Eu não vou entrar em detalhes sobre esse tipo de teste nesse post, mas você pode ler mais sobre isso aqui.

Quanto testar?

“Tá, Yasmin, você mostrou os tipos, deixou bem claro para tomarmos cuidado com UI test, mas qual é a medida? Qual a proporção?”. A resposta é sempre a mesma: depende. Muitos fatores podem influenciar na suite de teste: o tipo de projeto, a arquitetura, o seu objetivo, a cultura da empresa, etc.

No entanto, existem algumas convenções. Mike Cohn apresentou uma pirâmide de priorização ao falar de Agile e essa pirâmide foi adaptada para o ecossistema mobile.

O intuito é ter muitos testes unitários para garantir o funcionamento das pequenas partes, alguns testes de integração para garantir que essas pequenas partes conversem e por fim, uma quantidade menor de testes de interface para garantir que todos os sistemas se comuniquem corretamente e que a integridade dos dados seja mantida entre esses sistemas.

Infelizmente, o cenário real é bem diferente disso. Como falamos lá no começo, testes são constantemente despriorizados e acabamos com um arranjo que foi apelidado de “software testing ice cream cone”.

Os testes manuais não são assertivos e um bug pode ter acontecido por uma série de razões. Quanto mais subimos na pirâmide, mais difícil fica de apontar a causa de um problema. Quem nunca ouviu um “eu não sei o que eu estava fazendo, mas quando cliquei no botão tal o app fechou”. Por esses e outros motivos que devemos sempre tentar aumentar a cobertura do nosso código utilizando unit e integration tests.

UFA! Quanta informação! Espero que você tenha gostado, que esse post abra um pouquinho a sua mente e que você comece a olhar para testes com mais carinho. Eu sei que nem sempre conseguimos conciliar as entregas, mas essa é uma cultura que precisamos começar a trazer cada vez mais pros projetos e empresas. E podemos super aplicar esse conhecimento nos nossos projetos pessoais ;)

Ainda vou escrever os próximos três posts sobre injeção de dependências, arquitetura clean swift e um exemplo prático disso tudo, ok? Então acompanha aqui e qualquer dúvida é só mandar nos comentários.

Cheers!

Quer receber conteúdos exclusivos criados pelos nossos times de tecnologia? Inscreva-se.

--

--

Yasmin Benatti
iFood Tech

Howdy! I’m Yasmin Benatti Co-host @ MovileCast 🎙 Host @ Movile Tech Women Slow writer iOS dev @ifooduniverso DIYer whenever possible