Concrete Logo
Hamburger button

Criando um app iOS da Marvel – Parte 2

  • Blog
  • 23 de Dezembro de 2016
Share

Esse post foi originalmente publicado (em inglês) na Cocoa Academy, no Medium. Veja aqui.

Essa é a segunda parte de uma série de posts na qual pretendo abordar como criar um app iOS do início, usando diversos pods e ferramentas que tornam a nossa vida mais fácil. Se você ainda não viu a primeira parte, é só clicar aqui. Neste post vou falar sobre testes, relatórios de cobertura e como automatizar o processo usando o Fastlane, entre outras coisas.

O código-fonte desse projeto está disponível neste repositório. Eu criei uma tag para esse post, a v0.2, então você só precisa clonar o repositório e trocar para a tag v0.2. Ok?

Primeiros passos

Antes de tudo é melhor ter certeza que o projeto tem um target para testes unitários. Se não for o caso, nós podemos criar um só clicando em File > New > Target:

Pods de Teste

Para criar os testes para valer, vou usar várias libraries diferentes. Elas podem nos ajudar bastante no processo de escrever testes, então vamos adicioná-las ao Podfile. Abaixo você pode ver alguns pods como Quick, Nimble e Fakery, que vão melhorar muito a forma como nós lidamos com os testes.

Depois disso, podemos modificar o teste inicial, adicionando a ele nossos pods de testes. Vamos manter tudo bem simples por enquanto, pra ter certeza que está rodando direitinho.

Esse teste não faz muita coisa, apenas cria uma expectativa, true igual a true, o que sempre é verdade. Pode parecer simples, mas isso serve a um propósito maior. Garantir que tudo está configurado e funcionando conforme esperado.

As pessoas que até agora só escreveram testes usando o XCTest podem achar um pouco estranha essa forma de escrever, usando describes e expects ao invés de XCAssert. Entretanto, é muito mais próximo da linguagem natural, o que pode aumentar consideravelmente a legibilidade dos seus testes. É por isso que as pessoas escolhem essa forma, depois da curva de aprendizado escrever testes fica muito mais fácil e mais intuitivo, o que aumenta a adoção no seu projeto.

Uma coisa é certa: se os testes forem difíceis de escrever, ninguém vai escrever! O objetivo final dos frameworks de testes como Quick e Nimble é diminuir a curva de aprendizado o suficiente para que as pessoas escrevam.

Próximo passo: automatizar antes de complicar

Antes de adicionar qualquer outro teste, vamos ter certeza que nós podemos rodar os que já existem e gerar  o relatório de cobertura do código da linha cmd. Com isso, vamos garantir que teremos tudo configurado do jeito certo, enquanto a complexidade ainda é baixa. Assim, se alguma coisa não estiver certa, nós teremos poucos pontos para procurar o que está acontecendo.

Esse passo é muito importante, pois automatizando nosso pipeline de desenvolvimento nós podemos garantir que mais tarde vai ser muito mais fácil adicionar um processo de integração contínua.

Fastlane é uma ferramenta incrível, que pode nos ajudar com isso. Hoje vamos usar duas de suas actions, Scan para rodar os testes e o Slather para gerar o relatório de cobertura.

Vamos com calma! Mais algumas configurações

Antes de entrar no processo de automação, vamos garantir que todo mundo está na mesma página, o que significa ter certeza de que estamos usando as mesmas versões do Fastlane, Cocoapods, etc. Nós podemos fazer isso com o Bundler. Vamos criar um Gemfile como esse aqui:

E agora nós podemos rodar o cmd bundle para fazer o download e instalar essas dependências específicas:

Automatizando com Fastlane

O Fastlane é muito intuitivo. Tudo o que precisamos fazer é rodar o cmd e seguir os passos para setup. Depois que você fizer isso, vai ver uma pasta nova chamada fastlane, com alguns arquivos. Vamos focar só no fastfile por enquanto.

Seu Fastfile gerado vai ter mais algumas coisas, mas para este blog post vamos nos preocupar só com a “test lane”. Essa “lane” vai rodar seus testes usando as actions de Scan e gerar um relatório de cobertura usando Slather, parecido com esse aqui:

A foto mostra um relatório de cobertura do projeto na tag v0.2, com 97.16%. Nada mal, né? Estamos chegando lá =)

Agora, os testes

Vamos começar com uma camada modelo. Assim, podemos mapear a resposta da API e conseguimos carregar o json nos nossos testes e criar nossos modelos a partir dele. Para isso, vamos criar no nosso teste target uma struct chamada MockLoader.

Essa struct pode lidar com a complexidade de carregar um arquivo json definido no nosso projeto. Ela também é capaz de parsear o json em um modelo usando nosso framework de mapping, o ObjectMapper. Agora, nós só precisamos criar arquivos json e usar o MockLoader para recuperar essas informações em nossos testes.

O próximo passo é criar o primeiro spec de verdade. Vamos criar um CharacterSpec para testar nosso primeiro modelo.

Não tem muita coisa acontecendo aqui, mas mesmo esse pequeno teste pode atestar que o nosso modelo pode ser usado para mapear nossa resposta à API. Nós podemos afirmar isso com confiança, mesmo sem ter realizado até o momento qualquer chamada  à API. Isso mostra o quão poderosos e úteis os testes podem ser.

Testar esse tipo de coisa manualmente, sem testes automatizados, além de repetitivo é  bem propenso a erros. Existem muitos pontos no caminho nos quais as coisas podem sair do controle e se comportar de forma inesperada, como problemas de internet, com as camadas de rede, erros de chamada à API, etc. Os testes podem nos ajudar a evitar tudo isso e focar no que realmente queremos que, neste caso, é criar um modelo a partir de um json.

Não vou cobrir todos os testes nesse projeto, se não esse post vai ficar enorme. Mas eu sugiro que você clone o projeto e confira todos eles. Agora vamos passar para o próximo tipo de teste, as view controllers.

Testando View Controllers

Aqui tem um exemplo maior, com um monte de coisa acontecendo. Vamos abordar a essência e falar sobre algumas partes importantes.

O bloco de beforeEach é o lugar onde podemos configurar as coisas necessárias para todas as assertivas e expectativas que vamos criar.

Primeiro precisamos carregar nosso controller, o que queremos testar, e nós podemos fazer isso facilmente usando o enum Storyboard. Depois, precisamos mockar a API,  isso garantirá que nós estaremos rodando sem nenhuma dependência externa, como rede, por exemplo. Nós podemos fazer isso usando um setter injection. Deste momento em diante, as chamadas à API vão ser tratadas pela nossa classe de mock, que vai retornar o Json do arquivo.

Tem uma coisa importante sobre testes unitários: eles devem rodar em um ambiente controlado, o que significa sem chamadas de rede, sem acesso a banco de dados, etc.

Tem mais uma coisa que precisamos fazer como parte da receita para garantir que todos os nossos componentes de view sejam carregados. A instrução abaixo tem essa intenção:

Os outros testes são intuitivos e mudarão muito de projeto para projeto, por isso não vou cobri-los em detalhes. Entretanto, tem uma lição importante aqui. Você já ficou perdido, se perguntando sobre o que testar depois? A resposta é simples: procure por dicas no relatório de cobertura. O relatório vai mostrar áreas e funções do seu projeto que não estão cobertas. Para mim, essa é a melhor função de um relatório de cobertura. É muito mais que uma métrica por si só e deveria ser usada para guiar seus testes, assim como seus testes deveriam ser usados para refatorar e melhorar seu código.

Aviso: ** Se você está vindo do Objective-C, como eu, você deve estar se perguntando por que é que não estou usando um framework de mock. Acredite, eu me perguntei sobre isso também. Embora o Swift tenha alguns pods de mock, se você está adotando protocolos no seu código você não vai realmente precisar deles. Tudo o que você precisa é criar classes customizadas e structs no seu target de teste que estejam de acordo com o protocolo, assim como fiz acima, e substituir a implementação real por essa. Dessa forma tudo vai ficar mais simples. É verdade que algumas coisas são difíceis de fazer em Swift, o velho “parcial mock”  do OCMock era super útil. Dito isso, a comunidade Swift tem uma filosofia de testar só coisas públicas, o que faz sentido. Então, se você está tendo uma experiência ruim com seus testes, talvez o que você realmente precisa é focar em refatorar a implementação.

Testando datasources

Esse projeto usa uma técnica chamada de (na falta de um nome melhor) “external datasource”. De fato é só um jeito de remover a lógica de datasources e delegates da view controller, evitando a criação de “massive” view controllers no processo, que possuem muita responsabilidade. Esse tipo de design tem outros benefícios, como já contei na parte 1 desse tutorial.

Abaixo estão os testes dos datasources e delegates, não tem nada de realmente novo.

Pra terminar

Neste post eu usei várias técnicas e ferramentas que você pode incorporar ao seu projeto para testá-lo e melhorar a sua qualidade. Os testes criam uma “safety net” para o seu código, uma que você realmente pode confiar em qualquer momento. Daqui a seis meses, quando ninguém lembrar dos detalhes da implementação, você tem confiança para mudá-la, se os testes não falharem tudo estará funcionando como deveria, e isso por si só não tem preço.

No próximo post vou falar sobre integração contínua e porque você deveria querer em seu projeto. Também vou falar sobre o Danger e como ele pode ser usado para melhorar a colaboração no seu time dentro do seu repositório.

Aviso: ** É muito importante para seguir esses passos clonar o projeto e brincar com ele, pois eu não vou conseguir colocar todas as informações em um post só, ok? Ficaria gigante e inteligível.

Como sempre, feedbacks, dúvidas e opiniões são bem-vindas =)

É desenvolvedor iOS e quer fazer parte do nosso time? Clique aqui.