Olá! Meu nome é Priscila Brambila.
Audiodescrição: Priscila é uma mulher branca, com cabelo ruivo, usa óculos e veste uma camiseta preta.
Hoje, vamos aprender a realizar testes unitários de widget (ferramenta) e de integração.
No curso de hoje, vamos compreender melhor o que são cada um desses testes. A partir da aplicação vista no curso anterior de arquitetura, veremos como construir todos eles de forma modularizada e organizada, começando pelos mais simples e isolados até chegar aos testes de integração, que cobrem toda a nossa aplicação. Se desejamos aprender a lidar com testes e realizá-los da melhor forma possível, vamos seguir juntos para entender melhor como isso funciona. [♪]
Hoje vamos discutir sobre testes unitários e as ferramentas de desenvolvimento disponíveis no Flutter. Ao concluir o desenvolvimento de um aplicativo, é comum acreditar que todas as funcionalidades foram testadas e estão funcionando corretamente. No entanto, pode haver incertezas sobre se todos os cenários foram realmente testados. Mesmo afirmando que o aplicativo está 100% funcional, pode haver dúvidas internas sobre a integridade de todas as funcionalidades.
Com o tempo, é possível que bugs apareçam ou que alterações em funcionalidades que estavam funcionando perfeitamente acabem afetando outras partes do aplicativo. Durante a manutenção do código ou ao adicionar novas funcionalidades, pode-se perceber que, embora algo tenha funcionado inicialmente, não mantém o funcionamento perfeito o tempo todo. É nesse contexto que os testes se tornam essenciais.
Os testes na aplicação criam cenários necessários para cada funcionalidade, permitindo que elas sejam testadas individualmente e em conjunto. Isso garante que, ao realizar manutenção ou adicionar novas funcionalidades, nada será comprometido. Além disso, os testes são úteis para a documentação, pois mostram os casos de uso da aplicação. Assim, eles servem como uma forma de documentar o que foi feito, facilitando o trabalho de outras pessoas desenvolvedoras que venham a trabalhar no projeto.
No Flutter, existem diferentes tipos de testes. Começamos com os testes unitários, que geralmente testam regras de negócio de forma isolada, como funções clássicas e lógicas específicas. Por exemplo, se há uma função de soma na aplicação, testar essa função isoladamente é um teste unitário, pois está focado apenas nessa função, sem envolver telas, widgets ou qualquer elemento visual.
Para realizar testes unitários no Flutter, utilizamos o pacote test
. Este pacote é voltado para testes unitários dentro do Flutter, conforme descrito na documentação oficial.
Falando em aspectos visuais, vamos agora para os testes de widgets, que testam a interface do usuário (UI) de forma isolada, sem executar o aplicativo. O que isso significa? Ele testa desde um botão de forma isolada, para verificar se o clique aciona a função específica associada a ele, até mesmo se uma tela com um formulário está preenchendo todos os campos corretamente e, por exemplo, habilitando o botão de submissão no final. Isso caracteriza um teste de widget.
Para os testes de widgets, utilizamos o flutter_test
, que também é uma biblioteca específica do Flutter.
Por que especificamente sem executar o aplicativo? Porque, na verdade, ele não testa todas as funcionalidades integradas, como chamadas de API e outras interações externas. Ali, apenas a parte onde um widget interage com outro e transmite os dados ou interações necessárias é testada, sem a interação com classes ou elementos externos.
Agora, vamos entrar no terceiro tipo de teste, que é o teste de integração. O teste de integração simula o uso geral do aplicativo, com cliques, navegação entre telas e tudo mais. Nesse caso, não é necessário simular uma funcionalidade substituta para testar, como fazemos no teste de widget. Aqui, realizamos o teste real do aplicativo, com cliques, navegação, chamadas de API e outros elementos. É um tipo de teste específico no Flutter e, para quem vem de outras stacks, seria equivalente a um teste de ponta a ponta (end-to-end).
Para os testes de integração, utilizamos o integration_test
, que é utilizado para testes de integração ou end-to-end, caso você esteja mais familiarizado com esse termo em outras stacks. Essa ferramenta facilita muito o desenvolvimento desses testes.
Agora que já conhecemos um pouco sobre essas ferramentas de teste e os tipos de testes, vamos ver na prática como isso funciona.
Para entender melhor como os testes funcionam, vamos revisitar nosso projeto desenvolvido no curso anterior, relacionado à arquitetura. Vamos passar rapidamente por ele para compreender seu funcionamento, como algumas partes do código operavam e tudo mais, para que fiquemos contextualizados com o que será feito após nossos testes.
Considerando nosso projeto Focus, ele possuía basicamente três modos: o modo Focus, pausa curta e pausa longa. Na prática, os três realizavam a mesma função, mas com tempos diferentes. O modo Focus inicializa o temporizador para 25 minutos, oferecendo a opção de parar completamente ou pausar. Se clicarmos em pausar e depois continuar, ele retoma de onde parou. Caso optemos por parar, ele mostra o tempo em que foi interrompido, mas, ao inicializá-lo novamente, ele reinicia do zero.
Os outros dois modos funcionam exatamente da mesma forma, mas com tempos diferentes associados a cada tipo de pausa.
Agora, passando para o nosso código, vamos começar pelos enums, onde temos os tipos de timers. Como mencionamos anteriormente, eles funcionam, em geral, da mesma forma, mudando basicamente o nome, o tempo associado a eles e a imagem que está associada a cada tela. Cada tela possui sua própria imagem: o modo Foco, a pausa curta e a pausa longa.
Para representar esses modos, utilizamos um enum
chamado TimerType
. Vamos ver como ele é definido:
enum TimerType {
focus('Modo Foco', 25, 'assets/focus.png'),
shortBreak('Pausa Curta', 5, 'assets/pause.png'),
longBreak('Pausa Longa', 15, 'assets/long.png');
const TimerType(this.title, this.minutes, this.imageName);
final String title;
final int minutes;
final String imageName;
}
Considerando isso, temos também a nossa classe AppConfig
, que contém todas as constantes utilizadas no nosso aplicativo, permitindo que sejam acessadas de forma mais fácil, todas de um mesmo lugar. Vamos dar uma olhada na definição dessa classe:
import 'package:flutter/material.dart';
class AppConfig {
// Tempos em minutos
static const int focusTime = 25;
static const int shortBreakTime = 5;
static const int longBreakTime = 15;
// Cores do tema
static const Color backgroundColor = Color(0xFF021123); // Background padrão
static const Color buttonColor = Color(0xFFB072FF); // Cor dos botões
// Títulos
static const String appTitle = 'Fokus';
static const String focusTitle = 'Modo Foco';
static const String shortBreakTitle = 'Pausa Curta';
static const String longBreakTitle = 'Pausa Longa';
// Imagens
static const String logoImage = 'assets/logo.png';
static const String homeImage = 'assets/home.png';
static const String focusImage = 'assets/focus.png';
static const String pauseImage = 'assets/pause.png';
}
Outra funcionalidade importante é o nosso ViewModel
do timer, que é responsável por gerenciar a interação do usuário com a tela. Quando clicamos para inicializar o timer, a tela precisa responder indicando que o timer está rodando, ou, quando paramos ou pausamos, que ele parou. Vamos ver como isso é implementado:
import 'dart:async';
import 'package:flutter/material.dart';
class TimerViewModel extends ChangeNotifier {
bool isPlaying = false;
Timer? _timer;
Duration duration = Duration.zero;
void startTimer(int initialMinutes, ValueNotifier<bool> isPaused) {
duration = Duration.zero;
isPlaying = true;
notifyListeners();
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
if (isPaused.value) return;
if (duration.inMinutes < initialMinutes) {
duration = Duration(seconds: duration.inSeconds + 1);
notifyListeners();
} else {
stopTime();
}
});
}
void stopTime() {
_timer?.cancel();
isPlaying = false;
notifyListeners();
}
}
Depois disso, temos os widgets. O nosso widget do timer é o bloco que contém os botões de iniciar, continuar, pausar ou parar. A nossa tela, chamada TimerPage
, inclui também a imagem correspondente. Aqui está um exemplo de como os botões de controle são implementados:
// Botões de controle
SizedBox(
width: double.infinity,
height: 50,
child: ListenableBuilder(
listenable: timerViewModel,
builder: (context, child) {
bool isPlaying = timerViewModel.isPlaying;
return ElevatedButton(
onPressed: () {
if (isPlaying) {
timerViewModel.stopTime();
} else {
timerViewModel.startTimer(
widget.initialMinutes,
isPausedNotifier,
);
}
isPausedNotifier.value = false;
},
);
},
),
)
Além disso, temos a HomePage
, que contém os três botões, a imagem inicial e toda a tela inicial. Vamos ver como a HomePage
é estruturada:
import 'package:flutter/material.dart';
import 'package:fokus/shared/enums/timer_type.dart';
import 'package:fokus/shared/utils/app_config.dart';
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppConfig.backgroundColor,
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(AppConfig.logoImage, height: 80),
const SizedBox(height: 24),
Expanded(child: Image.asset(AppConfig.homeImage)),
const SizedBox(height: 40),
Column(
// Aqui você pode adicionar os botões e outros widgets
),
],
),
),
),
);
}
}
Com essa aplicação, vamos entender hoje como funcionam as três camadas de teste: os unitários, os de widget e os de integração.
O curso Flutter: Estratégias de Teste e Depuração possui 156 minutos de vídeos, em um total de 32 atividades. Gostou? Conheça nossos outros cursos de Flutter em Mobile, ou leia nossos artigos de Mobile.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Assine o PLUS e garanta:
Mobile, Programação, Front-end, DevOps, UX & Design, Marketing Digital, Data Science, Inovação & Gestão, Inteligência Artificial
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você participa de eventos exclusivos, pode tirar dúvidas em estudos colaborativos e ainda conta com mentorias em grupo com especialistas de diversas áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Para estudantes ultra comprometidos atingirem seu objetivo mais rápido.
1 ano de Alura
Todos os benefícios do PRO e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.
Conecte-se ao mercado com mentoria personalizada, vagas exclusivas e networking estratégico que impulsionam sua carreira tech para o próximo nível.