Testes unitários no iOS sem medo de ser feliz
É difícil falar de testes unitários no iOS sem falar sobre as dores envolvidas, como a lentidão para abrir o simulador, para compilar e instalar o app e só então começar a rodar os testes. Fora outras eventualidades que atrapalham o fluxo de trabalho.
Neste post vou falar o que tenho feito para me livrar dessas dores e tentar ser mais feliz.
A ideia basicamente consiste em fugir do iOS. Testar no iOS é ruim e chato? Vamos tentar testar no macOS.
Comece criando um Cocoa Framework, um framework para macOS. Aqui é importante lembrar que só podemos utilizar dependências que sejam compatíveis com macOS e iOS.
Após selecionar Cocoa Framework, dê um nome ao seu framework, selecione a linguagem que irá utilizar e marque a opção Include Unit Tests.
Agora temos um framework macOS onde vamos criar componentes como Repositories, Interactors, Services - coloque sua camada aqui - nesse target.
Faça testes unitários para essas camadas no testing bundle criado junto com os passos anteriores e agora conseguimos executar os testes de uma maneira muita mais simples rápida, que envolve apenas compilar o target e rodar os testes, sem mais simulador, sem mais instalação do app.
Você provavelmente utiliza algum padrão para a interação com a View, como MVP, MVC, MVVM… e talvez algo para abstrair a navegação do app, como um Router ou Coordinator. Você pode definir contratos e protocolos no framework macOS para essas camadas, mas a implementação da View e da navegação vão ficar no seu target do app iOS.
Vamos ver como isso ficaria na prática. No framework macOS vamos definir os seguintes protocolos:
Podemos criar uma implementação simples de MoviesViewModel
com o seguinte código:
Conseguimos testar nosso MoviesDefaultViewModel
no target macOS, da mesma forma que testamos no iOS:
Para utilizar o novo framework no app iOS, primeiramente é preciso criar um framework iOS. Para isso, criamos um Cocoa Touch Framework. Neste framework não é necessário marcar a opção Include Unit Tests, pois os testes estarão no testing bundle do macOS.
Os frameworks macOS e iOS precisam ter nomes diferentes, mas na hora que forem utilizados, o nome do módulo que será importado deverá ser o mesmo. Para isso, precisamos trocar o Product Name em Build Settings do framework iOS para o mesmo do framework macOS.
Para utilizar o seu framework no app iOS, adicione o framework iOS no target do app iOS em Embedded Binaries.
Adicione todos os arquivos que foram criados no framework macOS, no framework iOS e agora já é possível utilizar o framework no seu app. Basta criar as Views e componentes de navegação, que conformem com os protocolos definidos no framework.
Você pode conferir a implementação completa deste exemplo no github.
Além dos testes independentes do iOS, agora também conseguimos melhorar a abstração dos nossos dados e das nossas camadas. No Swift, o controle de acesso padrão a membros é o internal
que torna o membro visível para todo o módulo. Como estamos criando um framework, conseguimos um melhor controle de acesso, já que apenas os membros marcados como public
serão visíveis para o mundo externo.
E assim conseguimos um feedback muito mais rápido dos nossos testes, sem depender do ecossistema do iOS.