How do you unittest exceptions in Dart?
DartDart Problem Overview
Consider a function that does some exception handling based on the arguments passed:
List range(start, stop) {
if (start >= stop) {
throw new ArgumentError("start must be less than stop");
}
// remainder of function
}
How do I test that the right kind of exception is raised?
Dart Solutions
Solution 1 - Dart
In this case, there are various ways to test the exception. To simply test that an unspecific exception is raised:
expect(() => range(5, 5), throwsException);
to test that the right type of exception is raised:
there are several predefined matchers for general purposes like throwsArgumentError
, throwsRangeError
, throwsUnsupportedError
, etc.. for types for which no predefined matcher exists, you can use TypeMatcher<T>
.
expect(() => range(5, 2), throwsA(TypeMatcher<IndexError>()));
to ensure that no exception is raised:
expect(() => range(5, 10), returnsNormally);
to test the exception type and exception message:
expect(() => range(5, 3),
throwsA(predicate((e) => e is ArgumentError && e.message == 'start must be less than stop')));
here is another way to do this:
expect(() => range(5, 3),
throwsA(allOf(isArgumentError, predicate((e) => e.message == 'start must be less than stop'))));
(Thanks to Graham Wheeler at Google for the last 2 solutions).
Solution 2 - Dart
I like this approach:
test('when start > stop', () {
try {
range(5, 3);
} on ArgumentError catch(e) {
expect(e.message, 'start must be less than stop');
return;
}
throw new ExpectException("Expected ArgumentError");
});
Solution 3 - Dart
As a more elegant solution to @Shailen Tuli's proposal, if you want to expect an error with a specific message, you can use having
.
In this situation, you are looking for something like this:
expect(
() => range(5, 3),
throwsA(
isA<ArgumentError>().having(
(error) => error.message, // The feature you want to check.
'message', // The description of the feature.
'start must be less than stop', // The error message.
),
),
);
Solution 4 - Dart
An exception is checked using throwsA
with TypeMatcher
.
Note: isInstanceOf is now deprecated.
List range(start, stop) {
if (start >= stop) {
throw new ArgumentError("start must be less than stop");
}
// remainder of function
}
test("check argument error", () {
expect(() => range(1, 2), throwsA(TypeMatcher<ArgumentError>()));
});
Solution 5 - Dart
While the other answers are definitely valid, APIs like TypeMatcher<Type>()
are deprecated now, and you have to use isA<TypeOfException>()
.
For instance, what was previously,
expect(() => range(5, 2), throwsA(TypeMatcher<IndexError>()));
Will now be
expect(() => range(5, 2), throwsA(isA<IndexError>()));
Solution 6 - Dart
For simple exception testing, I prefer to use the static method API:
Expect.throws(
// test condition
(){
throw new Exception('code I expect to throw');
},
// exception object inspection
(err) => err is Exception
);
Solution 7 - Dart
I used the following approach:
First you need to pass in your method as a lambda into the expect function:
expect(() => method(...), ...)
Secondly you need to use throwsA
in combination with isInstanceOf
.
throwsA
makes sure that an exception is thrown, and with isInstanceOf
you can check if the correct Exception was thrown.
Example for my unit test:
expect(() => parser.parse(raw), throwsA(isInstanceOf<FailedCRCCheck>()));
Hope this helps.