3 minutos
Adeus mockito
Na saga da criação do aplicativo petnotes, acabei caindo em um problema com os testes. O aplicativo possui um conjunto de testes unitários, e alguns destes testes necessitam mockar dependências externas(requisições a bancos de dados ou API, por exemplo).
O problema veio quando realizei atualizações do Flutter e este começou a utilizar a nova funcionalidade do dart que trata valores null, basicamente foi necessária uma reescrita de parte do código para trabalhar com essa funcionalidade, o problema mesmo foi que todos os testes que faziam uso do pacote mockito passaram a não funcionar. Existiam alguns possíveis caminhos mas todos se mostraram muito trabalhosos ou simplesmente não funcionaram, foi aí que depois de alguma busca cheguei no pacote chamado mocktail.
O pacote tinha acabado de chegar na sua versão 1.0, e parecia um bom momento para iniciar o uso do mesmo. Vamos considerar um teste de exemplo que usa um banco de dados, escrevendo este teste com o mockito teríamos algo assim:
@GenerateMocks([Database])
void main() {
group('PetSQLite =>', () {
PetRepository petRepository;
Database mockDatabase;
setUp(() {
mockDatabase = new MockDatabase();
petRepository = new PetSQLite(mockDatabase);
});
group('create', () {
test('should add a pet to the database', () async {
final pet = Pet('name', 'specie');
when(mockDatabase.insert(any, any,
conflictAlgorithm: anyNamed('conflictAlgorithm')))
.thenAnswer((_) async {
return 1;
});
final response = await petRepository.create(pet);
expect(response, pet);
});
});
});
}
Neste teste estamos fazendo uso do any do pacote mockito para dizer que qualquer dado que for passado naquela posição a resposta do mock deve ser a mesma, é justamente o uso desse any que fez o pacote não funcionar nas novas versões do dart. Basicamente o teste a ser feito era converter o teste feito em mockito para o pacote mocktail. A conversão direta seria algo do tipo:
class MockDatabase extends Mock implements Database {}
void main() {
group('PetSQLite =>', () {
late PetRepository petRepository;
late Database mockDatabase;
setUp(() {
mockDatabase = new MockDatabase();
petRepository = new PetSQLite(mockDatabase);
});
group('create', () {
test('should add a pet to the database', () async {
final pet = Pet('name', 'specie');
when(
() => mockDatabase.insert(
any(),
any(),
conflictAlgorithm: any(named: 'conflictAlgorithm'),
),
).thenAnswer((_) async {
return 1;
});
final response = await petRepository.create(pet);
expect(response, pet);
});
});
;
});
}
Vemos aqui pequenas modificações no código:
- A geração do mock é feita herdando da classe Mock e não usando uma anotação que vai gerar um arquivo separado depois.
- Basicamente a mudança do ponto de vista do uso do pacote foi a forma do uso do any, aqui ele é uma função, e a mesma é utilizada também nos parâmetros nomeados.
- Ao passaro mock para a função when é necessário passar dentro de uma função.
Com essas mudanças o código passas a funcionar perfeitamente, particularmente achei a mudança de pacote mais fácil do que tentar implementar as correções necessárias para o mockito voltar a funcionar. Outro ponto interessante é que com o novo pacote, a quantidade de pacotes instalados simplesmente para rodar testes foi reduzida, com o mockito eram necessários dois pacotes.
O novo pacote se mostrou uma boa alternativa e provavelmente ficarei fazendo uso do mesmo por um bom tempo nos próximos projetos envolvendo dart.