Fala Devs, como estão?
Hoje vamos abordar um assunto um tanto quanto polêmico, amado por alguns odiado por outros, mas que é muito importante, se você não tem miopia e prestou atenção no título deste artigo já deve imaginar que iremos falar sobre Testes.
Sei que muitos desenvolvedores, me incluo nessa, odeiam fazer o famoso teste unitários, alguns nem tem ideia do que se trata, mas tentarei resumir de uma maneira bastante simplificada.
Testes Unitários
Basicamente existem vários tipos de testes (testes de performance, teste funcional, teste de integração etc.) nós programadores estamos muito acostumados a realizar o teste de unidade de uma maneira muito simplificada trata de um teste isolado a um componente ou classe que o desenvolvedor alterou ou criou, ou seja, você vai testar isoladamente aquilo que você mexeu, não se preocupando muito com o restante do sistema, se o que você fez quebrou o que já funcionava (esse teste será realizado na etapa de teste de integração).
Claro que todos sabemos que testar o que desenvolvemos é muito importante, mas testar manualmente um milhão de cenários é humanamente impossível desta maneira graças a evolução da tecnologia hoje conseguimos criar testes de maneira automatizadas, desta forma conseguimos testar vários cenários.
Como exemplo vamos criar um contador (que nem o do projeto de exemplo do exemplo do flutter) mas trabalharemos com o mobX, os testes não dependem exclusivamente do mobx, poderíamos usar BLOC ou qualquer outra coisa, mas vale ressaltar que é de suma importância começarmos a separar a lógica do design, desta maneira nossos testes ficaram muito mais fáceis de se escrever e muito mais organizados.
Desta forma teremos uma controller com dois métodos para incrementar e decrementar
Assim vamos criar nosso arquivo para testar se essa controller está fazendo o que realmente esperamos, para criar o arquivo de teste basta criar um arquivo na pasta test com o nome count_test.dart, é muito importante que o arquivo tenha o sufixo _test.
Se trata de um arquivo com código dart normal, temos que ter método main(), pois é nele que iremos adicionar os testes utilizando através da chamada test() que espera uma breve descrição do que se trata o teste e uma função de como iremos testar.
Nesse caso criei uma instância do HomeController e testei se o valor de count é igual a 0 (que é seu valor inicial) com a função expect. Logo em seguido executei o método add(), então esperamos que ele incremente 1 ao count, após realizamos o teste count verificando se ele vale 1. Desta maneira conseguimos executar o teste utilizando o comando flutter teste e ele irá me retornar se teve erro em algum teste.
VS CODE
Para quem utiliza o Visual Studio Code lá conseguimos instalar o plugin de testes, desta maneira conseguimos executar os testes apenas clicando em Run/Debug.
E conseguimos até “Debugar” nosso código executando linha a linha e verificar o que está ocorrendo em tempo de execução.
Mocks!?
Praticamente todos os sistemas existentes fazem algum tipo de integração com webservices ou banco de dados, e as vezes precisamos testar classes que dependem de buscar dados dessa integração, testar essas classes se tornam inconvenientes por algumas razões.
· Chamar webservices ou banco de dados “on the line” diminui a velocidade de execução dos testes.
· Um teste pode começar a falhar devido a instabilidades do webservice ou banco de dados e eles podem trazer resultados inesperados, isso é conhecido como “teste superficial”.
· É difícil testar todos os cenários possíveis de sucesso e falha usando um serviço da Web ou banco de dados ao vivo.
Portanto ao invés de depender de serviços web ou banco de dados “on the line” é mais prudente mockar essas dependências, os mocks permitem você simular essas dependências e retornar um valor específico.
Para fazer isso temos um package do flutter que se chama mockito, basta adicioná-lo em seu pubspec como dependência de desenvolvimento(creio que ele já venha por padrão).
IoC e DI
Para conseguirmos falar sobre MOCK é muito importante falarmos de Injeção de Dependência(DI) e Inversão de Controles (IoC), tem um vídeo muito interessante no canal da Flutterando.
Esses são temas que eu poderia escrever vários artigos a respeito, mas como não é o objetivo deste artigo tentarei ser sucinto. Lembrando que esses conceitos valem para qualquer linguagem e não especificamente o Flutter.
Suponha que eu tenha uma service que irá chamar minha classe de repository, então teoricamente basta eu criar uma variável do tipo AccountRepository e instanciá-la certo? Tudo muito lindo e funcionando, mas utilizando essa abordagem de deixar minha service com a responsabilidade de iniciar o AccountRepository teríamos alguns problemas.
Perceba que a classe AccountRepository precisa da urlBase para poder realizar a conexão com a API, agora vamos imaginar que essa URL se alterou para http://urlapi2.com, Oxe!? é só eu ir na AccountService e alterar, não? Certo, isso iria funcionar, mas vamos nos perguntar, o que a classe de repositório tem a ver com a classe de serviço? NADA!(ou pelo menos não devia) Inclusive criamos o aplicativo separando em services, repositories, controllers justamente por causa disso, cada camada tem sua responsabilidade.
Nesse caso é fácil modificar apenas um service, agora imagina se tivéssemos 30 services. E outra, a classe de serviço sabe que a classe de repositório necessita de uma url e isso não é justo já que a responsabilidade da classe de serviço é apenas aplicar as regras de negocio e chamar a repositório, que essa sim por sua vez teria a necessidade de se responsabilizar de integrar com a API, então nesse exemplo vemos um alto acoplamento nesse código.
E outro problema mais profundo, que está relacionado com o título deste artigo, é como faríamos, por exemplo, para fazer um teste com minha classe serviço e mockar minha dependência com o repositório? Simplesmente não iremos conseguir, pois a reponsabilidade de construir o repositório está a cargo do meu service.
É aqui que entra a inversão de controle (IoC), vamos literalmente inverter os controles em meu service, em vez de deixarmos a responsabilidade da criação do repositório na minha service vamos já conceder ao service minha repository montada, deste jeito vamos injetar as dependências que a service necessita e é ai que entra a magica da injeção de dependências (DI).
Essa é uma forma, e uma das mais populares, de utilizar injeção de dependência, o Constructor Injection, ou seja, injetamos uma dependência de uma classe através do construtor dessa classe.
Há muitas ferramentas que nos auxiliam na criação de projetos com injeção de dependências e um deles adivinha qual é? Isso mesmo, o Flutter Modular, basta adicioná-la no meu app_module.
Na Injeção de Dependências existem vários padrões que podemos utilizar na DI, um deles é o padrão por contrato utilizando interfaces, eu sei no dart não tenho interfaces, mas conseguimos simular uma interface e funciona muito bem também, eu particularmente gosto de me organizar criando uma pasta interfaces dentro de cada camada e lá teremos nossos arquivos de interfaces.
Para criarmos interfaces no DART (como ele não tem interface) criamos utilizando classes abstratas, contendo todos os contratos de métodos que terá na minha classe concreta.
Desta forma basta eu implementar a classe abstrata na minha classe concreta
E lá na minha classe de serviço que chamava a AccountRepository eu não preciso mais chamar ela diretamente, posso trocar minha dependência apontando para interface, dessa maneira eu posso injetar uma classe MOCKADA quando for fazer os testes.
Ufâ! Assim conseguimos resumir bem o que é IoC e DI. E não se esqueça! se ficou alguma dúvida, deixe um comentário.
MOCK
Agora voltando para nosso assunto principal, vamos criar uma classe “fake” que retornara nossos dados mockados.
Uma outra forma de mockarmos um dado é importar os json na pasta assets
Precisamos também liberar o acesso aos arquivos lá no pubspec.yaml
E lá na minha classe de Mock carregamos esse arquivo e utilizamos o método fromJson() da minha Model para construir o resultado.
Desta forma deixamos nosso Mock mais fiel ao repositório original
Lá no modulo precisamos injetar a dependência do repositório. Desta maneira fica a responsabilidade de dizer se ele vai utilizar o Repository ou o Mock a quem for utilizar o service.
setUp
Agora que criamos as injeções de dependências podemos utilizar na prática, para isso nos testes no DART temos um método que chama setUp() que serve para inicializarmos os atributos dos meus testes, no exemplo a seguir vamos criar uma Controller (PayslipController) e vamos injetar a PayslipService e o mock da PayslipRepository.
Dessa maneira conseguimos injetar todas as dependências necessária para utilizar a PayslipController, fácil né? Mas tudo pode ficar mais fácil ainda com o Flutter Modular, para isso eu posso utilizar o método initModule() que já me traz todas as dependências do projeto, e para trocar a instancia o repositório para a classe de mock basta eu utilizar o atributo changeBinds.
Testes de Widgets
No pacote flutter_test temos uma opção bastante legal que não vemos em todos os frameworks por ae, que é a possibilidade de testarmos os widgets para isso basta utilizarmos a função testWidgets() e podemos utilizar da seguinte forma. (Este é o exemplo do contador)
Na primeira linha do teste ele utiliza o pumpWidget() para inicializar o MyApp() que é a tela principal do APP, em seguida eu verifico se eu encontro um widget com o texto o texto “0” na tela e se não há nenhum widget com o texto “1”.
Logo em seguida eu o mando procurar um widget que contenha um ícone de adicionar (+) e logo em seguida eu peço para ele pressionar “tap” o botão.
Eu uso o pump() para aguardar ele renderizar a tela com seu novo estado e faço as verificações novamente, verifica se não existe um widget com o valor “0” (findsNothing) e se eu encontro um widget com valor “1” (findsOneWidget).
“That’s all folks!”. De uma maneira bem resumida é isso que conseguimos fazer utilizando a biblioteca de testes do Flutter, pode ser um pouco trabalhoso no início mas garanto que isso trará muitos benefícios conforme o projeto vai crescendo.
Entre em nosso discord para interagir com a comunidade: https://discord.com/invite/flutterbrasil
https://linktr.ee/flutterbrasil
Top comments (0)