Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# 2.0.2
- Update dependencies
- Update `very_good_analysis` to `9.0.0`
- Added topics: [test, testing, parameterized] to pubspec.yaml
- Added small explanation to README.md about Future.error usage in tests.
- Updated dart formatting.

## 2.0.1
- Updated dependencies
- sdk to `>=3.4.0 <4.0.0`
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,10 @@ parameterizedTest('Example of CSV data',
});
```

### Testing with `Future.error` values
When using `Future.error` directly in your parameterized test values, this might leads to not executing tests at all. In order to handle this correctly your can provide the `Future` as a callback function.
But also need to make sure that the when the future is awaited its also catch. `Future.error` will throw the provided object. See [23](https://github.com/DutchCodingCompany/parameterized_test/issues/23) for more information.

## Additional information 💡

It's just a simple wrapper to easily execute tests multiple times with different values. Feel free to
Expand Down
3 changes: 3 additions & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
include: package:very_good_analysis/analysis_options.yaml

formatter:
trailing_commas: automate

analyzer:
errors:
unawaited_futures: warning
Expand Down
105 changes: 50 additions & 55 deletions example/parameterized_test_example.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// ignore this lint for example reasons.
// ignore_for_file: avoid_print

import 'package:csv/csv.dart';
Expand All @@ -6,46 +7,44 @@ import 'package:test/test.dart';

void main() {
// Simple test containing a list of single values
parameterizedTest('Example of list of single values', [1, 2, 3], (int value) {
final result = value < 4;
expect(result, true);
});

// Simple test containing a list of multiple values
parameterizedTest(
'Example of list of single values',
'Example of list of multiple values',
[
1,
2,
3,
[0, 1, 1],
[1, 1, 2],
[1, 2, 3],
[2, 2, 4],
],
(int value) {
final result = value < 4;
expect(result, true);
(int value1, int value2, int sum) {
expect(value1 + value2, sum);
},
);

// Simple test containing a list of multiple values
parameterizedTest('Example of list of multiple values', [
[0, 1, 1],
[1, 1, 2],
[1, 2, 3],
[2, 2, 4],
], (int value1, int value2, int sum) {
expect(value1 + value2, sum);
});

// Test containing a list with complex objects
parameterizedTest('Example of a list with complex object', [
[DateTime(2024, 4, 12), 5],
[DateTime(1969, 07, 20), 7],
], (DateTime dateTime, int expectedWeekday) {
expect(dateTime.weekday, expectedWeekday);
});

// Test containing a list of enums
parameterizedTest(
'Example using enum as value',
FruitEnum.values,
(FruitEnum testEnum) {
expect(testEnum.name.length, testEnum.wordLength);
'Example of a list with complex object',
[
[DateTime(2024, 4, 12), 5],
[DateTime(1969, 07, 20), 7],
],
(DateTime dateTime, int expectedWeekday) {
expect(dateTime.weekday, expectedWeekday);
},
);

// Test containing a list of enums
parameterizedTest('Example using enum as value', FruitEnum.values, (
FruitEnum testEnum,
) {
expect(testEnum.name.length, testEnum.wordLength);
});

// Test retreiving the list of values from a function
List<dynamic> provideData() {
return [
Expand All @@ -56,13 +55,13 @@ void main() {
];
}

parameterizedTest(
'Example of list of values from function',
provideData(),
(int value1, int value2, int sum) {
expect(value1 + value2, sum);
},
);
parameterizedTest('Example of list of values from function', provideData(), (
int value1,
int value2,
int sum,
) {
expect(value1 + value2, sum);
});

// Simple test with setup and teardown
parameterizedTest(
Expand All @@ -88,28 +87,24 @@ void main() {
// But this is not a good practice to use a delay like
// this in a test. Running this test will take longer. This could be
// fixed by using a package like fake_async.
parameterizedTest(
'Example using a async test',
[
100,
200,
300,
],
(int value) async {
final millis = DateTime.now().millisecondsSinceEpoch;
await Future<void>.delayed(Duration(milliseconds: value));
final passed = DateTime.now().millisecondsSinceEpoch - millis;
parameterizedTest('Example using a async test', [100, 200, 300], (
int value,
) async {
final millis = DateTime.now().millisecondsSinceEpoch;
await Future<void>.delayed(Duration(milliseconds: value));
final passed = DateTime.now().millisecondsSinceEpoch - millis;

expect(passed >= value, true);
},
);
expect(passed >= value, true);
});

// Test with CSV data
parameterizedTest('Example of CSV data',
const CsvToListConverter().convert('kiwi,4\r\napple,5\r\nbanana,6'),
(String fruit, int length) {
expect(fruit.length, length);
});
parameterizedTest(
'Example of CSV data',
const CsvToListConverter().convert('kiwi,4\r\napple,5\r\nbanana,6'),
(String fruit, int length) {
expect(fruit.length, length);
},
);
}

enum FruitEnum {
Expand Down
2 changes: 1 addition & 1 deletion lib/parameterized_test.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/// A library that simplifies writing parameterized tests in Dart.
library parameterized_test;
library;

export '/src/parameterized_test.dart'
show parameterizedGroup, parameterizedTest;
Expand Down
36 changes: 16 additions & 20 deletions lib/src/errors/parameterized_error.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,16 @@ class ParameterizedError extends Error {

/// Creates a [ParameterizedError] from a [TypeError].
/// When arguments types don't match the function arguments types.
factory ParameterizedError.forTypeError(
List<dynamic> value,
Function body,
) {
factory ParameterizedError.forTypeError(List<dynamic> value, Function body) {
final bodyClosure = extractFunctionArgumentsSignature(body.toString());

return ParameterizedError(
"Provided value(s) didn't match the function arguments types.\n"
'Test values: $value\n'
'Provided types: '
'(${value.map((e) => e.runtimeType).join(', ')})\n'
'Expected types: ($bodyClosure)');
"Provided value(s) didn't match the function arguments types.\n"
'Test values: $value\n'
'Provided types: '
'(${value.map((e) => e.runtimeType).join(', ')})\n'
'Expected types: ($bodyClosure)',
);
}

/// Creates a [ParameterizedError] from a [TypeError].
Expand All @@ -32,13 +30,14 @@ class ParameterizedError extends Error {
final positionalArgumentsCount = ','.allMatches(bodyClosure).length + 1;

return ParameterizedError(
"Provided value(s) didn't match the function arguments count.\n"
'Amount of provided values: ${value.length}\n'
'Expected function arguments: $positionalArgumentsCount\n'
'Test values: $value\n'
'Provided types: '
'(${value.map((e) => e.runtimeType).join(', ')})\n'
'Expected types: ($bodyClosure)');
"Provided value(s) didn't match the function arguments count.\n"
'Amount of provided values: ${value.length}\n'
'Expected function arguments: $positionalArgumentsCount\n'
'Test values: $value\n'
'Provided types: '
'(${value.map((e) => e.runtimeType).join(', ')})\n'
'Expected types: ($bodyClosure)',
);
}

/// Error message.
Expand All @@ -51,10 +50,7 @@ class ParameterizedError extends Error {
final closureIndex = body.indexOf(closure);
final endIndex = body.indexOf(')', closureIndex);

final bodyClosure = body.substring(
closureIndex + closure.length,
endIndex,
);
final bodyClosure = body.substring(closureIndex + closure.length, endIndex);

return bodyClosure.replaceAll(RegExp('<(.*)>'), '');
}
Expand Down
4 changes: 1 addition & 3 deletions lib/src/errors/stack_trace_extension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ extension StackTraceExtension on StackTrace {

final frames = trace.frames.take(index).toList();

return !frames.every(
(f) => f.package == 'parameterized_test' || f.isCore,
);
return !frames.every((f) => f.package == 'parameterized_test' || f.isCore);
}
}
58 changes: 24 additions & 34 deletions lib/src/parameterized_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ import 'package:parameterized_test/src/test_options/value_with_test_options.dart
import 'package:test/test.dart';

/// Callback signature for test and group functions.
typedef TestFunc = void Function(
Object? description,
dynamic Function() body, {
String? testOn,
Timeout? timeout,
Object? skip,
Object? tags,
Map<String, dynamic>? onPlatform,
int? retry,
});
typedef TestFunc =
void Function(
Object? description,
dynamic Function() body, {
String? testOn,
Timeout? timeout,
Object? skip,
Object? tags,
Map<String, dynamic>? onPlatform,
int? retry,
});

/// Callback signature for setUp and tearDown functions.
typedef SetupFunc = void Function(dynamic Function());
Expand Down Expand Up @@ -42,12 +43,8 @@ typedef SetupFunc = void Function(dynamic Function());
/// ```
/// {@endtemplate}
@isTestGroup
ParameterizedTest get parameterizedTest => const ParameterizedTestImpl(
group,
test,
setUp,
tearDown,
);
ParameterizedTest get parameterizedTest =>
const ParameterizedTestImpl(group, test, setUp, tearDown);

/// {@template parameterizedGroup}
// ignore: comment_references
Expand Down Expand Up @@ -76,12 +73,8 @@ ParameterizedTest get parameterizedTest => const ParameterizedTestImpl(
/// ```
/// {@endtemplate}
@isTestGroup
ParameterizedTest get parameterizedGroup => const ParameterizedTestImpl(
group,
group,
setUp,
tearDown,
);
ParameterizedTest get parameterizedGroup =>
const ParameterizedTestImpl(group, group, setUp, tearDown);

/// {@template ParameterizedTest}
/// Implementation of parameterized test.
Expand Down Expand Up @@ -159,17 +152,16 @@ class ParameterizedTestImpl implements ParameterizedTest {
testDescription,
() {
try {
return Function.apply(
body,
value,
);
return Function.apply(body, value);
}
// We want to catch these errors to provide better error messages
//ignore: avoid_catching_errors
on NoSuchMethodError catch (e, s) {
if (s.isInsideTestBody) rethrow;

throw ParameterizedError.fromNoSuchMethodError(e, value);
}
// We want to catch these errors to provide better error messages
//ignore: avoid_catching_errors
on TypeError catch (e, s) {
if (s.isInsideTestBody) rethrow;
Expand Down Expand Up @@ -206,9 +198,7 @@ class ParameterizedTestImpl implements ParameterizedTest {
List<dynamic> value,
CustomDescriptionBuilder? customDescriptionBuilder,
) {
final mappedValues = value.map(
(e) => e is String ? "'$e'" : e.toString(),
);
final mappedValues = value.map((e) => e is String ? "'$e'" : e.toString());

return customDescriptionBuilder?.call(description, index, value) ??
'[ ${mappedValues.join(', ')} ]';
Expand All @@ -217,9 +207,9 @@ class ParameterizedTestImpl implements ParameterizedTest {
/// Unpack wrapped values and test options.
/// Handle different types of values.
ValueWithTestOptions parseValues(dynamic value) => switch (value) {
ValueWithTestOptions() => value,
List() => (values: value, testOptions: null),
Iterable() => (values: value.toList(), testOptions: null),
_ => (values: [value], testOptions: null),
};
ValueWithTestOptions() => value,
List() => (values: value, testOptions: null),
Iterable() => (values: value.toList(), testOptions: null),
_ => (values: [value], testOptions: null),
};
}
Loading