Guia minimalista para testes no Flutter — Parte 2 Mocking

Tempo de leitura: 4 minutes

Basicamente, temos 2 opções de pacotes principais no Flutter

  • Mockito
  • Mocktail

Ambos são ótimos pacotes, mas vou optar pelo mocktail

 

Por que escolhi Mocktail?

Não me interpretem mal, o mockito também é ótimo e desenvolvido pela própria equipe de dardos! Mas eu simplesmente amei a abordagem do mocktail! Porque;

  • API mais simples
  • Sem geração de código
  • Type Safety

Só isso!

 

Por que precisamos de pacotes, não podemos escrever sem nenhum pacote?
Na verdade, podemos, mas acho que esta imagem explicará muito bem a situação para você.

MOTIVAÇÃO
Mesmo se neste exemplo básico você vê claramente a diferença!

Além disso, também precisamos escrever todos esses códigos para todos os cenários! Pense nesse clichê!

De qualquer forma

 

O que é Mocking, Por que precisamos disso?

“A ideia principal por trás do teste de unidade é isolar e focar na unidade atual que estamos testando e não no comportamento de dependências externas. Mas, na maioria dos casos, precisamos depender de dependências externas como banco de dados, servidores web, API de plataforma, dispositivos externos, etc.

Vamos supor que nossa unidade atual dependa de uma API da web. O teste funciona devagar, mas bem quando o servidor está ativo. Mas quando o servidor está offline, o teste de unidade falha. Isso torna o teste de unidade imprevisível. Porque o servidor web não estará sob nosso controle. Não é nossa culpa quando o servidor web cai. É aqui que entra o mocking.

Portanto, apenas criamos serviços falsos para isolar nosso teste de outras dependências e assumimos que todas as dependências externas funcionam corretamente!

Você pode pensar que basicamente; substituindo/falsificando dependências com os originais

Por que não os chamamos de fingimento em vez de zombaria (instead of mocking)?

Na verdade, não sei o motivo principal, mas suponho que o serviço falso tenha um significado diferente. (como trabalhar em um ambiente falso), mas se usarmos falsificação em vez de zombaria. Os termos seriam misturados! NA MINHA HUMILDE OPINIÃO. Enfim, isso não importa!

Além disso, você verá muito do termo “stub” ao testar seu aplicativo!

O que é Stub?

Stub é um pedaço de código usado para substituir alguma outra funcionalidade de programação. Um stub pode simular o comportamento do código existente

Basicamente, pegamos a classe e criamos uma falsa que tem a mesma aparência, mas com uma funcionalidade diferente!

class Horse {}
class Trojan extends Mock implements Horse {}

Chega de tanta teoria?

Então, vamos dar uma olhada em nossos poderes ocultos!!

A lógica é simples!

“Diga ao seu Trojan o que fazer em todos os casos que acontecerem! E faça acontecer! E controle-o se fez o seu trabalho corretamente ou não!

// Stub the `sound` method.
when(() => trojan.sound()).thenReturn('horse sound');
// Verify no interactions have occurred.
verifyNever(() => trojan.sound());
// Interact with the mock trojan instance.
trojan.sound();
// Verify the interaction occurred.
verify(() => trojan.sound()).called(1);
// Interact with the mock instance again.
trojan.sound();
// Verify the interaction occurred twice.
verify(() => trojan.sound()).called(2);

Na verdade, temos 3 etapas principais durante o teste!

(esqueça as etapas init e descarte por enquanto!)

 

1. Organizar — Decida o comportamento!
Nossa palavra mágica é quando

“Quando esse método for executado, faça isso!”

// Sync Methods
when(() => service.syncMethod()).thenReturn('ehe');
// Async Methods
when(() => service.asyncMethod()).thenAnswer((e) async => 'ehe');
// Error Handling
when(() => service.brokenMethod()).thenThrow(Exception('ehe'));

2. Aja — Experimente!
Não há nenhum caso especial aqui, apenas executamos os métodos! Se precisarmos!

final res = service.syncMethod();
final res2 = await service.asyncMethod();
final res3 = service.brokenMethod();

3. Afirme — confirme!
Por fim, garantimos que funcionou corretamente! Assim definimos nossas expectativas e as comparamos com os resultados!

// We make ensure that method never runned before!
verifyNever(() => service.syncMethod());
// we call it once and ask the app is it called only once?
verify(() => service.syncMethod()).called(1);
// we expect we run it but is it return this value?
expect(service.syncMethod(), 'ehe');
// I know it'll throw an expection but it's really throw it?
expect(() => service.brokenMethod(), throwsA(isA<Exception>()));

Você também pode redefinir seu serviço facilmente sempre que quiser!
e comece seu próximo teste facilmente!

reset(service);

 

Cuidado!

Se você quiser usar seus tipos personalizados com correspondências de argumento, você precisa registrar seu tipo no começo! Porque o Mocktail suporta apenas tipos primitivos prontos para uso! Mas não se preocupe, é muito fácil fazer isso!

class Food {...}
class Cat {
  bool likes(Food food) {...}
}
...
class MockCat extends Mock implements Cat {}
class FakeFood extends Fake implements Food {}
test('...', () {
  registerFallbackValue(FakeFood());
  final cat = MockCat();
  when(() => cat.likes(any<FakeFood>()).thenReturn(true);
  ...
});

 

Por que não consigo fazer stub/verify métodos de extensão corretamente?

Também copiei diretamente da documentação porque explica muito bem a situação!

“Os métodos de extensão não podem ser stubbed/verified, pois são tratados como métodos estáticos. Isso significa que as chamadas vão diretamente para o método de extensão sem se preocupar com a instância. Como resultado, stubs e chamadas de verificação para extensões sempre resultam em uma invocação do método de extensão real.

Em vez de stubbing/verifying métodos de extensão diretamente, prefira stub/verify membros públicos na instância com a qual os métodos de extensão interagem.”

class MyClass {}
extension on MyClass {
  void foo() {}
}
class MockMyClass extends Mock implements MyClass {}

“No cenário acima, chamar foo no MockMyClass sempre invocará o foo() real (não um stub). Como resultado, não é possível fazer stub/verify métodos que são extensões porque eles sempre resultarão na chamada da extensão real.”

Isso é tudo que eu ia falar sobre mocking tests!

Semana que vem, vou começar a falar sobre os testes unitários e botar a mão na massa!