Что нового
  • Что бы вступить в ряды "Принятый кодер" Вам нужно:
    Написать 10 полезных сообщений или тем и Получить 10 симпатий.
    Для того кто не хочет терять время,может пожертвовать средства для поддержки сервеса, и вступить в ряды VIP на месяц, дополнительная информация в лс.

  • Пользаватели которые будут спамить, уходят в бан без предупреждения. Спам сообщения определяется администрацией и модератором.

  • Гость, Что бы Вы хотели увидеть на нашем Форуме? Изложить свои идеи и пожелания по улучшению форума Вы можете поделиться с нами здесь. ----> Перейдите сюда
  • Все пользователи не прошедшие проверку электронной почты будут заблокированы. Все вопросы с разблокировкой обращайтесь по адресу электронной почте : info@guardianelinks.com . Не пришло сообщение о проверке или о сбросе также сообщите нам.

Começando com testes no Flutter

Lomanu4

Команда форума
Администратор
Регистрация
1 Мар 2015
Сообщения
11,730
Баллы
155
O básico


O melhor lugar para começarmos é pelo básico, e um dos melhores exemplos para quem começou a aprender a desenvolver em Flutter é o projeto inicial, o famoso CounterApp, que é um projeto simples contendo apenas um botão de “+” e um contador que incrementamos ao pressionar o botão.

Assim que criamos um projeto temos a seguinte estrutura de código no nosso arquivo main.dart:


void main() {
runApp(const MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}

class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});

final String title;

@override
State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;

void _incrementCounter() {
setState(() {
_counter++;
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}

Então podemos prosseguir e analisar como podemos montar nossos testes para essa tela, e o projeto já nos dá uma breve introdução aos testes de widget por meio de arquivo test/widget_test.dart:


void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());

// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);

// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();

// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}

Esse é um teste bem simples, ele tem o único objetivo de verificar se ao pressionar o botão "+” o contador na tela é incrementado, mas como ele faz isso é o que vamos ver.

Como executar os testes


Antes de tudo para executarmos todos os testes que se encontram na pasta /test do nosso projeto basta executarmos o seguinte comando no terminal na pasta do projeto:


flutter test

Para executarmos apenas um arquivo de teste específico basta adicionar o caminho e nome do arquivo ao final:


flutter test test/widget_test.dart
main()


void main() {
..
}

A primeira coisa que conseguimos notar é que da mesma forma que nossa aplicação, nosso teste também é iniciado dentro de uma função main() , mas diferente da aplicação ele consegue identificar que é um teste simplesmente pelo nome do arquivo, então os arquivos que possuem test ao final do nome são considerados arquivos de teste, por isso ele não vai exigir um aparelho ou emulador para fazer a execução.

Da mesma forma se não nomearmos nosso arquivo de teste da maneira correta ele vai tentar executar nossos testes como uma aplicação, e claro esse não é o comportamento que esperamos.

testWidgets()


testWidgets('Counter increments smoke test', (WidgetTester tester) async {
...
});

Para iniciar um teste precisamos de um bloco de código com a única responsabilidade de executar nosso cenário especifico, e essa é a função do testWidgets() . E para que ela funcione precisamos de dois parâmetros:

description : a descrição do teste, por meio dele podemos esclarecer o que nosso teste faz.

callback : recebe o bloco de teste que será executado, sendo possível executar código assíncronos utilizando async/await .

Essa função foi criada para testar widgets, mas se no caso você deseja fazer apenas um teste unitário você pode usar a função test() que tem a mesma assinatura mas não tem acesso ao WidgetTester .


test('Counter increments smoke test', () {
...
});
tester.pumpWidget()


await tester.pumpWidget(const MyApp());

Essa é a função responsável por renderizar a UI do Widget recebido para o teste, ela vai fazer a chamada da função runApp() da mesma forma que é feita no arquivo main.dart para iniciar nossa aplicação. Por isso é necessário que o widget informado seja um MaterialApp.

Nesse cenário o teste vai funcionar normalmente, mas se precisamos testar outro widget que não possui o MaterialApp no início da árvore precisamos adicioná-lo.


await tester.pumpWidget(MaterialApp(home: const MySecondPage()));
expect()


expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);

Essa função é o ponto vital do nosso teste, ela que é responsável por dizer se nosso teste foi um sucesso ou uma falha. Se o valor esperado for igual ao valor recebido o teste é um sucesso. Para isso recebemos dois parâmetros, o primeiro é o valor ou variável que queremos verificar e o segundo é a condição que desejamos atender.

Se estiver comparando dois objetos com instâncias diferentes é necessário fazer o override da função == e do campo hasCode do objeto para considerar os valores dos campos ao invés da instância.
Dessa forma podemos ler a primeira chamada do expect da seguinte forma:

Eu espero que esse campo (find.text('0')) possua esse valor (findsOneWidget)
Mas o que significam esses campos?

A variável find é um objeto próprio da biblioteca de teste do flutter que nos permite ter acesso a várias funções que podemos usar para encontrarmos nossos widgets na tela, como por exemplo:

ancestor(...)
byElementPredicate(...)
byElementType(...)
byIcon(...)
byKey(...)
byType(...)
byWidget(...)
Em meio a quantidade de métodos podemos ficar perdidos sobre qual usar, mas o ideal aqui é sempre tentar encontrar o widget que queremos da forma mais simples possível, e é isso que é feito no teste de exemplo.

Ao declarar find.text('0') ele deseja encontrar qualquer widget na tela que possua um texto com o valor ‘0’, esse é o valor inicial do nosso contador ao iniciar a tela.

O campo findsOneWidget é o nosso matcher, ele é responsável por responder se existe pelo menos 1 widget com o valor que declaramos.

E podemos usar outras variações, como por exemplo:

  • findsNothing - espera que o finder não encontre nada
  • findsWidgets - espera que o finder encontre um ou mais widgets
  • findsNWidgets - espera que o finder encontre uma quantidade específica de widgets que nós informamos
  • findsAtLeastNWidgets - espera que o finder encontre pelo menos uma quantidade mínima de widgets que nós informamos

Agora ao voltarmos ao exemplo inicial podemos entender o seguinte:


expect(find.text('0'), findsOneWidget);
Espero que seja encontrado apenas um widget com o texto '0'
expect(find.text('1'), findsNothing);
Espero que não seja encontrado nenhum um widget com o texto '1'
tester.tap()


await tester.tap(find.byIcon(Icons.add));

A função tester.tap() tem um nome bem sugestivo, em casos onde queremos acionar o gesto de pressionar de algum widget é ele que faz isso.

Nesse teste por exemplo ele primeiro vai usar o finder para encontrar algum widget que usa o ícone Icons.add , e ao encontrar, ele vai acionar o tap ou pressioná-lo.

Após isso esperamos então que a nossa tela seja atualizada modificando o valor de "0” pra "1”, e não queremos encontrar o texto "0” e sim "1”.


expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);

Mas se executarmos nosso teste assim teremos um erro porque nossa função tap realizou uma mudança de estado na tela mas nosso widget não foi atualizado.

Vamos ver como resolver isso com a função tester.pump().

tester.pump()


await tester.pump();

Quando executamos um teste de widget ele é renderizado e os testes são executados em cima do frame inicial da tela, mas quando temos uma mudança de estado no nosso widget nosso teste precisa saber disso, e essa função faz isso, diz ao teste que o estado do widget precisa ser atualizado para o próximo frame.

Nesse exemplo, após pressionarmos o botão de "+” esperamos que o frame da tela seja atualizado para que o texto "0” seja substituído por "1”, então precisamos chamar essa função para que ele entenda que precisa fazer isso.


await tester.pump();

Também podemos passar para função uma duração se quisermos que a atualização seja feita após algum período.


await tester.pump(Duration(seconds: 5));

Ou podemos usar o pumpAndSettle() que vai esperar pelo próximo frame para continuar o teste.


await tester.pumpAndSettle();

Agora se executarmos o teste novamente ele vai ser concluído com sucesso.


void main() {
testWidgets('Deve verificar se o contador está sendo incrementado', (WidgetTester tester) async {
// Renderiza nosso widget
await tester.pumpWidget(const MyApp());

// Verifica se o contador é iniciado no 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);

// Pressiona o botão '+' para incrementar o contador e atualiza a tela
await tester.tap(find.byIcon(Icons.add));
await tester.pump();

// Verifica se o contador foi incrementado
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}
Conclusão


Conseguimos por meio desses testes garantir que a tela da nossa aplicação funcione da maneira correta e assim podemos evitar que caso sejam adicionados mais recursos, essa funcionalidade continue funcionando como o esperado.


Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

 
Вверх