Guia minimalista para testes em Flutter

Tempo de leitura: 6 minutes

https://methodpoet.com/test-driven-development-best-practices/Gostaria que você cumprimentasse a nova série de artigos sobre Testing in Flutter! Vou tentar cobrir todas as coisas em profundidade nesta série. Meu principal objetivo é aprender Testing corretamente e, ao aprendê-lo, criar o melhor guia para todos nós como um recurso! Espero que você ache útil e goste!

Sou novo neste tópico, portanto, se houver informações ausentes ou incorretas, informe-me e ajude-me a corrigi-lo! Desde já, obrigado!

 

Então vamos começar!

Se queremos levar a qualidade a sério, é hora de nos cansarmos de encontrar bugs e começarmos a prevenir que eles aconteçam em primeiro lugar — Robert Brunhage (grande cara)

 

O que é Testar?

Quando codificamos um aplicativo, o que vamos fazer primeiro?

Nós testamos! E olha só, está funcionando bem ou não! Se encontrarmos um bug, nós o corrigimos e continuamos a codificar, certo?

Esse é o teste, mas essa não é a única opção!
Existem dois tipos de testes por aí!

 

Teste manual (caixa preta):

O teste manual não requer nenhum conhecimento de programação ou algo assim. O testador apenas tenta quebrar o aplicativo manualmente sem saber qual algoritmo é executado dentro dele! É por isso que chamamos de teste de caixa preta! Não é tão confiável porque podemos encontrar comportamentos inesperados de outras partes ao adicionar novos recursos, e o testador pode não ser bom o suficiente (afinal, somos todos humanos) etc.

 

Teste automatizado (caixa branca):

Por outro lado, o teste automatizado precisa de uma linguagem de programação porque dizemos ao compilador como testar o aplicativo em todos os cenários e deixá-lo testar e garantir que o aplicativo funcione corretamente! É por isso que é mais confiável do que o teste manual! Além disso, é muito confiável porque é cumulativo, o que significa que o compilador também verifica todos os recursos existentes enquanto adiciona novos recursos. Todo o aplicativo será testado do início ao fim a cada alteração! E isso garante que o desenvolvimento atual não interrompa nenhum recurso existente.

 

Então, vamos dar uma olhada nos prós e contras do teste automatizado!

Prós

  • Mostra a qualidade do desenvolvedor!
    (algumas pessoas escrevem testes, mesmo que ninguém afirme isso. E isso os torna especiais e dignos de respeito!)
  • Melhora a qualidade do código e nos ajuda a escrever um código melhor
    (Força-nos a escrever um código melhor, como Responsabilidade Única, Arquitetura em Camadas, Injeção de Dependência, Abstração, etc.)
  • Ajuda a pensar de forma clara e básica, não complexa!
    (Por favor, ouse escrever código complexo e veja o que acontece ao escrever um teste de unidade)
  • Economiza muito tempo e dinheiro
    (Nos ajuda a identificar bugs no estágio inicial)
  • Ajuda-nos a manter a nossa sanidade e evita frustrações
    (Evita feedback desagradável de seu chefe e clientes!)
  • Evita bugs estranhos e inesperados
    (Quando adicionamos um novo recurso ou corrigimos um bug ou refatoramos o código antigo, podemos quebrar as outras funcionalidades ou até mesmo o aplicativo inteiro!)
  • Evita que fiquemos para trás!
    (Evita que fiquemos repetindo “antes estava funcionando!”)
  • A manutenção a longo prazo é mais fácil.
    (Apenas olhando para o teste, podemos entender facilmente do que se trata.)
  • Refatore com confiança
    (Você não precisa se preocupar em quebrar algumas coisas ao refatorar seu código. Mesmo se você quebrar, saberá qual e onde está o problema instantaneamente)
  • Torne a depuração ainda mais simples!
    (podemos saber exatamente os casos que estão falhando e causando o bug.)

 

Contras

  • Pode parecer um pouco demorado
    (Mas, não totalmente, porque mesmo que pareça retardar o tempo de desenvolvimento, pelo contrário, acelera totalmente porque reduz muitos bugs que ocorrerão no futuro)

E aqui estão alguns dos casos que testamos em uma unidade.

  • Verifique se os valores iniciais são verdadeiros
  • Verifique se o valor foi atualizado conforme o esperado
  • Verifique se o resultado do teste é o esperado
  • Verifique se a unidade lida com os erros conforme o esperado
  • Verifique se o teste de unidade funciona, mesmo se chamar mais de uma vez

Também

  • Verifique os casos nulos
  • Verifique se os tipos de dados são verdadeiros

(Graças ao sistema Dart’s null e type safety, não precisamos deles! Contanto que não usemos variáveis dinâmicas e anuláveis.)

 

Testando no Flutter

(Unit Test) Teste de Unidade — Teste a Lógica

  • Para testar uma única classe ou um único método/função para verificar uma unidade de lógica em condições definidas
    (execute o método e espere que funcione conforme o esperado)

(Widget Test) Teste de widget — Teste a interface do usuário

  • Para testar um único widget de interface do usuário e seu comportamento
    (clique no botão e espere que funcione conforme o esperado)

Golden Test — Teste a adaptabilidade e capacidade de resposta da interface do usuário

  • Os testes de ouro são basicamente testes de widgets, mas para verificar se a imagem “dourada” gerada e o widget são os mesmos.

(Integration Test) Teste de Integração — Teste a Experiência

  • Para testar grandes partes do aplicativo da perspectiva do usuário
    (como, preencher os campos de texto e clicar no botão de login e esperar a página inicial de roteamento, etc.)

(End-to-End (E2E) Test) Teste de ponta a ponta — Teste todo o sistema (front-end + back-end)

  • Para testar toda a experiência.

 

https://docs.flutter.dev/testing

 

Vamos ver um pouco de código!

Vou apenas mostrar os recursos que você pode fazer por enquanto!
Mas abordarei isso em profundidade nos próximos artigos, portanto, fique atento!

// You can define global rules using annotations!
// we have lots of platform options indeed!
// vm, chrome, firefox, safari, node, dart-vm, browser, js, mac-os, linux, android, ios...
@Tags(['browser', 'android'])
@TestOn('ios')
@Skip('currently failing (see issue 1234)')
@Timeout(Duration(seconds: 45))
@OnPlatform({'windows': Timeout.factor(2)})
void main() {
  
  late HttpServer server;
  
  // inits just before the tests getting started
  setUp(() async {
    server = await HttpServer.bind('localhost', 0);
  });
  
  // Disposes when all the tests over
  tearDown(() async {
    await server.close(force: true);
    server = null;
  });
  
  // Creates a test
  test('description', () {
    // write your test here
  });
  
  // you can group your tests in a scope like that!
  group(
    'description',
    () {
      test('test1', () {});
      test('test2', () {});
      test('test3', () {});
    },
    skip: 'reason',
  );
  
  // Also you can define tests specific rules too!
  test(
    'description', () {},
    // you can also define specific rules to specific tests!
    tags: 'chrome', // or ['chrome', 'firefox'],
    skip: 'reason', // skips the test
    timeout: const Timeout.factor(2), // slows down time by 2x
    retry: 3, // runs the test 3 times to make sure it's ok
    testOn: 'browser && !chrome', // Every browser but Chrome
    // platform specific rules!
    onPlatform: {
      'windows': const Timeout.factor(2),
      'browser': [const Skip('add browser support')],
    },
  );
  
  // and yeah we can expect anything as a result!
  // thanks to matchers package, we have lots of methods to match the results!
  expect(text.trim(), equals('foo')); // sync
  expect('foo,bar,baz', allOf([contains('foo'), isNot(startsWith('bar')), endsWith('baz')])); // sync
  expect(await Future.value(10), equals(10)); // async
  expectLater(actual, matcher); // async 
  expect(Future.value(10), completion(equals(10))); // async
  expect(Future.error('oh no'), throwsA(equals('oh no'))); // on error
  expect(Future.error(StateError('bad state')), throwsStateError); // on error
  expect(() => int.parse('X'), throwsFormatException); // on error
  expect(stream, emitsInOrder(['ready.', emitsAnyOf(['succeeded', 'failed']), emitsDone])); // stream
}

Para mais informações clique aqui.

 

Algumas informações aleatórias sobre o teste!

  • Os testes devem ser rápidos, simples, independentes, determinísticos, repetíveis, focados em apenas uma coisa e fáceis de entender!
    (Arrange-Act-Assert)
  • Repetição de código em testes é aceitável.
    (Um teste deve se concentrar mais na simplicidade do que nas boas práticas de codificação.)
  • Usar classes abstratas facilita nossa vida!
  • Os testes devem ser escritos durante a codificação! Não quando o aplicativo terminar completamente! (NA MINHA HUMILDE OPINIÃO)
  • O teste de serviço é questionável!
    (Na maioria das vezes, você não precisa escrever testes para os serviços que usa, mas se você não confia no cara do back-end, acho que você também deve escrever testes de serviço em seu aplicativo!)
  • Mocking é apenas uma simulação de dependências. Nada mais nada menos!
  • Os arquivos devem ser nomeados com o sufixo “_test.dart” em Dart Lang
  • TDD: Primeiro escreva seu teste, depois escreva seu código! (Falha-Refator-Sucesso)

 

11 Test Driven Development Best Practices – Tests That Think!

  • BDD: Escreva seus testes em uma linguagem legível por humanos (Given-When-Then)
BDD Example
Feature: Counter
Scenario: Increase and decrease the value of the counter
 Given
 Counter's initial value is 0
 And increase and decrease buttons are available
 
 When User click on the increase button.
 Then the counter's value should be 1.
  • Cobertura significa quão extensivamente testado é o seu código.

https://tech.andpad.co.jp/entry/2020/11/17/170000

 

Por último

Ninguém manda você escrever seus testes, mas os testes mostram a qualidade das pessoas! — Um cara sábio