[pt-BR] Comece pelo teste
Tags: testing, dart, flutter
Published: 2020-07-19 17:54:36

<small>Linguagem: [Dart](https://dart.dev)</small>

---

Em todas as profissões se testa, e isso tem total relação com o grau de qualidade que um produto pode ter para poder ser oferecido. Inclusive, em algumas áreas os testes são obrigatórios e a qualidade controlada por órgãos (ex: [Inmetro](https://www4.inmetro.gov.br/acesso-a-informacao/institucional)).

Vamos entender em seguida como funcionam os testes de software e como os testes podem nos ajudar a praticar a linguagem e aumentar a qualidade da entrega.

---

## Tipos de teste de software

![Tipos de teste de software](https://dev-to-uploads.s3.amazonaws.com/i/q64evwrcz3dtis32hfsl.png)

Existem muitas categorias de teste, cada um com suas vantagens, desvantagens e objetivos. Vou abordar neste post os tipos de testes recomendados na documentação do [Dart](https://dart.dev/guides/testing):

**Os testes unitários** tem a responsabilidade de garantir a integridade dos nossos métodos. *Um teste unitário deve ser rápido e barato de executar.*

**Os testes de componentes** são responsáveis pela parte de 'interfaces' e interações que um componente pode receber. *Normalmente garante a exibição de elementos e mudanças de estados.*

**Os testes de integração** testam a aplicação de uma visão mais completa, navegando e simulando comportamentos dos componentes em conjunto. *Verifica o comportamento da aplicação e também pode ser utilizado para medir desempenho.*

---

![Testes](https://dev-to-uploads.s3.amazonaws.com/i/i0em4o2hcexvhgoi35mh.png)

<small>(ref: https://flutter.dev/docs/testing)</small>

---

Seguindo esta ordem, considere o seguinte: maior quantidade de testes unitários, seguido por testes de componentes e de integração.

## Entrega

Quando você entrega seu trabalho testado para os outros profissionais revisarem (ex: 'code review'), isso economiza muito tempo das pessoas e o seu.

Com testes sua entrega sobe de nível e evita retorno de tarefas para correções (bugs).

> Sem testes, além de ficar preocupado (*despreocupado é pior*), você terá a necessidade de alcançar mais rápido seus objetivos para não ser atropelado por falhas e anomalias em códigos sem testes.

---

## TDD

Para escrever o primeiro teste e entender a vantagem de se testar, vamos utilizar uma técnica chamada *TDD* (ou desenvolvimento orientado a testes). O TDD, em resumo, é guiar seu desenvolvimento através de testes (vale pesquisar mais sobre o assunto).

Para chegarmos no resultado esperado, seguindo TDD, precisamos passar por três etapas principais:

* Escrever o teste e vê-lo falhar 🔴
* Desenvolver o método e ver o teste passar 🟢
* Melhorar o método com a garantia dos testes 🤓

**Repeat!**

## Primeiro teste

Em uma aplicação [Flutter](https://flutter.dev) a primeira coisa que eu penso em testar é a '*main*'. Será que tem o que testar lá?

Tem sim! Podemos executar funções antes do 'bootstrap' do nosso aplicativo e isso pode ser testado.

Considere o seguinte: precisamos que uma função seja executada antes do `runApp` do Flutter. Poderíamos ter colocado direto na 'main', mas alguém poderia alterar ou remover essa função e ela deixaria de ter o comportamento esperado.

A vantagem de um teste desse é justamente dele falhar se alguém alterar o código ou remover por engano. Outra vantagem é a descrição dos testes, que ajudam novos colaboradores a entender os casos de uso do código na aplicação.

---

Para entender as etapas deste primeiro teste é legal dar uma lida na documentação do Dart e no pacote *mockito*:

- [Dart testing](https://dart.dev/guides/testing)
- [Package: mockito/mockito](https://pub.dev/packages/mockito)

### O que será testado:
A ideia é testar a execução do `runner` da aplicação com a condição de executar uma função antes com as configurações de inicialização do projeto.

### O teste:
Antes de começar a escrever o teste em si, precisamos criar as classes de `Mock` para que apenas o método `run` seja executado no teste.

Dentro do teste executamos o método `run` (é o método que estamos testando) e o `verifyInOrder` para verificar a execução na ordem correta.

Ficou assim:

```dart
// imports...
// bootstrap file import

class FunctionsMock extends Mock {
  void runApp(dynamic widget) {}
  void before() {}
}

class ComponentMock extends Mock {}

void main() {
  test('App bootstrap method', () async {
    final fMock = FunctionsMock();
    final appMock = ComponentMock();

    await Bootstrap(appMock, fMock.before).run(fMock.runApp);
    verifyInOrder([fMock.before(), fMock.runApp(appMock)]);
  });
}

```

<small>*tests/bootstrap_test.dart*</small>

---

Chegou a hora de executar o teste e ver falhar:

```shell
$ flutter test
```

```shell
00:04 +0 -1: loading test/bootstrap_test.dart [E]
  Failed to load "test/bootstrap_test.dart":
  Unable to spawn isolate: test/bootstrap_test.dart:17:11: Error: Method not found: 'Bootstrap'.
      await Bootstrap(appMock, fMock.before).run(fMock.runApp);
            ^^^^^^^^^^^^
00:04 +0 -1: Some tests failed.
```

## Escrevendo o método
Vamos criar uma classe `Bootstrap` que recebe um `Widget` e uma função `before` no construtor.

O método `run` recebe um `runApp` (do Flutter) e executa a função `before` antes do `runner` com a nossa app (`Widget`).

```dart
// imports...

class Bootstrap {
  final Widget app;
  final Function before;

  Bootstrap(this.app, this.before);

  Future<void> run(Function(Widget) runner) async {
    await before();
    await runner(app);
  }
}

```

<small>*lib/bootstrap.dart*</small>

---

Utilizando a classe:

```dart
// imports...

void main() {
  Bootstrap(MyApp(), () { ... }).run(runApp);
}

// MyApp Widget Implementation
```

<small>*lib/main.dart*</small>

---

Seguindo esta ordem, escrevendo o teste primeiro, podemos definir o que esperamos do método. Executando o teste esperamos sempre que ele falhe e está falha vai guiar o desenvolvimento do método. *Faça o teste passar!*

```shell
$ flutter test
```
```shell
00:02 +1: All tests passed!
```

---

Note que não testamos o método `before`, isso deverá ser feito em outra descrição de teste.

Agora sua implementação começa a ficar mais segura e em caso de mudanças seu teste vai quebrar para a felicidade de todos.

> A ideia de aprender com os testes é que nos arquivos de testes vamos precisar lidar com a classe/método fora da sua aplicação e isso nos leva a melhorar essas classes/métodos, além de utilizar outras partes da linguagem.

---

## É apenas o começo

No caso do Flutter você pode seguir para os [testes de componentes](https://flutter.dev/docs/cookbook/testing/widget/introduction) (*widgets*). Para todos os casos, vale a leitura de toda [documentação do Dart](https://dart.dev/guides/testing) sobre testes.

Espero voltar com os próximos passos, mas não espere por mim, gogogo!