46

in short, throwsA(anything) does not suffice for me while unit testing in dart. How to I test for a specific error message or type?

Here is the error I would like to catch:

class MyCustErr implements Exception {
  String term;

  String errMsg() => 'You have already added a container with the id 
  $term. Duplicates are not allowed';

  MyCustErr({this.term});
}

here is the current assertion that passes, but would like to check for the error type above:

expect(() => operations.lookupOrderDetails(), throwsA(anything));

This is what I want to do:

expect(() => operations.lookupOrderDetails(), throwsA(MyCustErr));

xpeldev
  • 1,812
  • 2
  • 12
  • 23
  • 1
    You can check out this post for answers on how to match for specific messages as well: https://stackoverflow.com/questions/13298969/how-do-you-unittest-exceptions-in-dart – Almund Aug 19 '19 at 00:25

6 Answers6

76

This should do what you want:

expect(() => operations.lookupOrderDetails(), throwsA(isA<MyCustErr>()));

if you just want to check for exception check this answer:

Ber
  • 40,356
  • 16
  • 72
  • 88
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
33

As of April 2021 this is the correct method.

CORRECT METHOD

import 'package:dcli/dcli.dart';
import 'package:test/test.dart';

 /// GOOD: works in all circumstances.
 expect(() => restoreFile(file), throwsA(isA<RestoreFileException>()));

Some examples show :

INCORRECT METHOD

import 'package:dcli/dcli.dart';
import 'package:test/test.dart';
 /// BAD: works but not in all circumstances
 expect(restoreFile(file), throwsA(isA<RestoreFileException>()));

Note the missing '() => ' after the expect.

The difference is that the first method will work for functions that return void whilst the second method won't.

So the first method should be the prefered technique.

To test for a specific error message:

CHECK CONTENTS OF EXCEPTION AS WELL

import 'package:dcli/dcli.dart';
import 'package:test/test.dart';

    expect(
        () => copy(from, to),
        throwsA(predicate((e) =>
            e is CopyException &&
            e.message == 'The from file ${truepath(from)} does not exists.')));
Brett Sutton
  • 3,900
  • 2
  • 28
  • 53
  • This certainly works but there's no logical reason why should one pass lambdas for it to work! This is straight-up unintuitive syntax comparing to something like JUnit. – Arrowsome May 01 '21 at 18:41
  • @Arrow if the method being called returns null then you must use () otherwise you get a compile error: This expression has a type of 'void' so its value can't be used. ... I do have lints turned up to 11 which might be why you see a difference. – Brett Sutton May 02 '21 at 23:29
17

After `TypeMatcher<>' has been deprecated in Flutter 1.12.1 I found this to work:

expect(() => operations.lookupOrderDetails(), throwsA(isInstanceOf<MyCustErr>()));
Ber
  • 40,356
  • 16
  • 72
  • 88
  • 4
    `isInstanceOf` has been deprecated as well, in favor of `isA`. So the code should now be `expect(() => operations.lookupOrderDetails(), throwsA(isA()));` – rlat Jul 11 '20 at 21:24
2

In case anyone wants to test with an async function like I had to do all you need to do is add async keyword in the expect, bearing in mind that the lookupOrderDetails is an async function:

expect(() **async** => **await** operations.lookupOrderDetails(), throwsA(const TypeMatcher<MyCustErr>()));

expect(() **async** => **await** operations.lookupOrderDetails(), isInstanceOf<MyCustErr>()));

It still uses Gunter's answer which is very good!

Mihai Neagoe
  • 257
  • 2
  • 4
  • 14
  • 5
    Not sure why, but neither of those variants worked for me, however `expect(() async => await resultFuture, throwsA(isInstanceOf()));` did – Matt R Oct 14 '19 at 10:50
1

First import correct package 'package:matcher/matcher.dart';

expect(() => yourOperation.yourMethod(),
      throwsA(const TypeMatcher<YourException>()));
1

The current proper way to expect that a function call throws an exception is:

expect(operations.lookupOrderDetails, throwsA(isA<MyCustErr>()));`