Can Dart or Flutter unit tests be automatically repeated? - flutter

When unit testing with Dart or Flutter
If there are 100 unit tests, if you run the flutter test command, 100 test codes will be tested once.
But I want to repeat this N times.
Is there a command or way to automatically start the test again after one full test and report if the test is broken?

I'm not sure why this would be necessary, but I suppose you could wrap the tests you want to repeat in a for loop from 0 to N.
If you define N using int.fromEnvironment then you can pass in a value for N at the command line.
flutter test --dart-define=N=100
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:example/main.dart';
const N = int.fromEnvironment('N', defaultValue: 1);
void main() {
for (int i = 0; i < N; i++) {
testWidgets('Counter increments smoke test $i',
(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);
});
}
}
Alternatively you could write a program that runs flutter test N times.

Related

Flutter integration_test loads forever

I am trying to run a very simple integration_test but it is loading forever. Any ideas about what is going wrong here?
import 'package:advicer/2_application/pages/advice/widgets/advice_field.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:advicer/main.dart' as app;
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('end-to-end', () {
testWidgets('tap on custom button, verify advice will be loaded',
(widgetTester) async {
app.main();
await widgetTester.pumpAndSettle();
//verify that no advice has been loaded
expect(find.text('Your advice is waiting for you'), findsOneWidget);
//find custom button
final customButtonFinder = find.text('get advice');
//emulate a tap on the custom button
await widgetTester.tap(customButtonFinder);
//Trigger a frame and wait until its settled
await widgetTester.pumpAndSettle();
//verify the advice is loaded
expect(find.byType(AdviceField), findsOneWidget);
});
});
}
softPug#liruhuadeMacBook-Pro Dart_Flutter_zero-master % cd advicer
softPug#liruhuadeMacBook-Pro advicer % flutter test integration_test00:00 +0: loading /Users/softPug/Documents/GitHub/Dart_Flutter_zero-master/advicer/integration_test/advicer_app_test.dart                 R06:33 +0: loading /Users/softPug/Documents/GitHub/Dart_Flutter_zero-master/advicer/integration_test/advicer_app_test.dart                ⣽
Trouble shoots my code to successfully run integration test

Flutter tests - Having two Integration tests in a same group ()

I'm trying to run two tests in a same group in one test file.
If I split them into two separate test files, they are working fine.
But if I add them in a group, the 2nd test fail with the error saying "TypeAdapter is already registered for the given id".
My app uses HiveDB and it sets up Hive boxes in the main.dart before launching the App. I understand that the 2nd test is also trying the same and fails because the setup is already done.
I followed this doc to setup and write integration tests.
Most of the youtube tutorials, medium articles, and other online resources explain so well on how to run one single test in a test file and they're outdated. I never found a resource having two tests in the same file yet.
Here's my main.dart
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
await HiveDB.setup();
runApp(App());
}
test_driver/integration_test.dart
import 'package:integration_test/integration_test_driver.dart';
Future<void> main() => integrationDriver();
test/app_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:integrationtestdemo/main.dart' as app;
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('Home screen tests', () {
tearDown(() async {
print("TearDown() -- called");
await HiveDB.close();
});
testWidgets('Scenario # - loreum ipsum ...', (tester) async {
// ARRANGE
await app.main();
await tester.pumpAndSettle();
// ACT
// ASSERT
expect(1 + 3, 4);
});
testWidgets('Scenario #2', (tester) async {
// ARRANGE
await app.main();
await tester.pumpAndSettle();
// ACT
// ASSERT
expect(2 + 2, 4);
});
});
}
The command I use to execute tests:
flutter drive --driver=.\test_driver\integration_test.dart --target=.\test\app_test.dart
Error I get:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞═════════════════
The following HiveError was thrown running a test:
There is already a TypeAdapter for typeId 1.
When the exception was thrown, this was the stack:
#0 TypeRegistryImpl.registerAdapter (package:hive/src/registry/type_registry_impl.dart:104:11)
#1 HiveDB.setup (package:occasionly/src/services/hive_db.dart:80:10)
<asynchronous suspension>
Could someone please help me here? I'm stuck on this for a long time and couldn't figure out a way to isolate the state between tests?
I have found a dirty trick to solve "TypeAdapter is already registered for the given id" this error
try to put it like this, and the error will not appear anymore
Hive.registerAdapter(YourAdapter(), override: true);
But I have no idea what is the possible impact could this argument do
I also faced the same issue, but I found a workaround by referring to this ERROR in flutter: widget_test.dart cannot detect MyApp(), you could try it.
testWidgets('Scenario #1', (tester) async {
// ARRANGE
await app.main();
...
});
testWidgets('Scenario #2', (tester) async {
// ARRANGE
await tester.pumpWidget(MyApp());
});

How to pass parameters into flutter integration_test?

I used flutter_driver before for integration tests and was able to insert parameters to the test via environment variables from the host, as the test was running from the host.
For another project I am now using the integration_test package.
The test is not running any longer on the host but on the target so when trying to pass arguments via environment variables, the test does not get them.
I saw https://github.com/flutter/flutter/issues/76852 which I think could help but are there other options available right now?
If you're using the integration_test package, the test code can set global variables prior to running your app, and pull them from the environment specified using --dart-define
For example:
// In main.dart
var environment = 'production';
void main() {
if (environment == 'development') {
// setup configuration for you application
}
runApp(const MyApp());
}
// In your integration_test.dart
import 'package:my_app/main.dart' as app;
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
setUpAll(() {
var testingEnvironment = const String.fromEnvironment('TESTING_ENVIRONMENT');
if (testingEnvironment != null) {
app.environment = testingEnvironment;
}
});
testWidgets('my test', (WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();
// Perform your test
});
}
then use the command line flutter test integration_test.dart --dart-define TESTING_ENVIRONMENT=development
Alternately, you could pull them from String.fromEnvironment directly in your app code.
I had the same problem running integration_tests on android emulator. A look at the documentation of bool.fromEnvironment() showed:
/// This constructor is only guaranteed to work when invoked as `const`.
/// It may work as a non-constant invocation on some platforms ...
So this works for me testing with android:
const skipFirst = bool.fromEnvironment('SKIP_FIRST');

Flutter integration test for UI and HTTP call

I have a simple login screen that calls login API and I trying to wait for the second screen after ProgressIndicator finishes and the new screen comes.
But It shows loading ProgressIndicator then it never goes to the second screen and expects function fails
So what should I do or what is similar to waitFor but for Flutter integration_test library?
My testing code
await tester.tap(find.byKey(const Key('loginButton')));
await tester.pumpAndSettle();
await tester.ensureVisible(find.byKey(const Key('loadingWidget')));
await tester.pumpAndSettle();
await tester.ensureVisible(find.byType(DetailsScreen));
expect(find.text('Log In'), findsNothing);
Here is the login button onTap
BlocProvider.of<LoginBloc>(context).add(
LoginEvent.loginWithEmail(
email: emailController.text,
password: passwordController.text,
),
);
You should use pumpAndSettle method of WidgetTester to wait until animations end. Your code will be like this:
testWidgets("Login test", (tester) async {
...
await tester.tap(find.byKey(ValueKey('Login')));
await tester.pumpAndSettle();
...
}
My goal is integration test so I replaced integration_test with the default Flutter driver and I was able to waitFor while animation by using
await driver.runUnsynchronized(() async {
//wait for something
});
More info: https://github.com/flutter/flutter/issues/36244
And I mock my Dio HTTP client by the following library
http_mock_adapter

How do I run Flutter widget tests with shadows enabled?

I'd like to render some of my Flutter golden files with shadows. How can I accomplish that?
Shadows are disabled by default:
https://github.com/flutter/flutter/blob/master/packages/flutter_test/lib/src/binding.dart#L942
With the debugDisableShadows flag.
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('my golden test with shadows enabled', (tester) async {
// Enable shadows
debugDisableShadows = false;
await tester.pumpWidget(MyWidget());
await expectLater(find.byType(MyWidget), matchesGoldenFile('..'));
// Set the flag back to normal
debugDisableShadows = true;
});
}
Note that you have to toggle the flag back to normal inside the test case (and not setUp / tearDown) - otherwise, it will fail.
This happens because this check is performed immediately after the body of testWidgets() completes, but before the test case is considered finished. Which is also before tearDown() is executed.