A Dart testing utility for asserting that some code emits a compilation error.

Overview

The expect_error package is a testing library inspired by Typescript's // @espect-error, designed to help package authors to test compilation errors.

Usage

expect_error exposes a compiles matcher, which can be used within unit tests.

A simple example would be:

// test/my_test.dart
import 'package:expect_error/expect_error.dart';
import 'package:test/test.dart';

void main() async {
  final library = await Library.parseFromStacktrace();

  test('String is not assignable to int', () async {
    await expectLater(library.withCode('''
// expect-error: INVALID_ASSIGNMENT
int value = "string";
'''), compiles);
  });
}

This example tests that the code:

int value = "string";

emits the compilation error "INVALID_ASSIGNMENT".

FAQ: Why use a comment in the code block instead of a matcher?

You may wonder why:

await expectLater(library.withCode('''
// expect-error: INVALID_ASSIGNMENT
int value = "string";
'''), compiles);

is preferrable to:

await expectLater(library.withCode('''
int value = "string";
'''), throwsCompilationError('INVALID_ASSIGNMENT'));

The reason why expect_error relies on a comment is because a comment doesn't simply communicate what the error is, but also where that error is.
When using // expect-error: x, only the next line is allowed to emit the compilation error.

As such, if we do:

await expectLater(library.withCode('''
void main() {
  // expect-error: INVALID_ASSIGNMENT
  print('a');
  int value = "string";
}
'''), compiles);

Then this test will fail. Because while the code block indeed contains an INVALID_ASSIGMENT error, the error isn't on the print but instead on the int value = 'string'.

To fix out test, we would have to move the comment on the line before the error:

await expectLater(library.withCode('''
void main() {
  print('a');
  // expect-error: INVALID_ASSIGNMENT
  int value = "string";
}
'''), compiles);

Specifying multiple error codes at the same time.

It is possible for expect-error to specify multiple codes at once by separating them with a ,:

await expectLater(library.withCode(r'''
String fn(int a) => '';

// expect-error: NOT_ENOUGH_POSITIONAL_ARGUMENTS, INVALID_ASSIGNMENT
int a = fn();
'''), compiles);

Importing files/packages in code blocks

It is possible to use import directives to import dart code within code blocks:

await expectLater(library.withCode(r'''
import 'package:riverpod/riverpod.dart';

final provider = Provider
   
    ((ref) {
   
  // expect-error: INVALID_ASSIGNMENT
  return 'string';
});
'''), compiles);

The imports available within our code blocks are dependent on that library variable. When we do:

void main() async {
  final library = await Library.parseFromStacktrace();

  test('...', () async {
    await expectLater(library.withCode(...), compiles);
  });
}

that library variable we created tells expect_error what imports the tested code block can use.

In particular, Library.parseFromStacktrace() makes our tested code behave as if it was defined in a separate file within the same folder our test file.

As such, the following code is valid too:

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

// our test file can use relative import to import another file
import 'relative.dart';

void main() async {
  final library = await Library.parseFromStacktrace();

  test('...', () async {
    await expectLater(library.withCode('''
// our tested code can also import the same file
import 'relative.dart';
'''), compiles);
  });
}

Flutter support

Unfortunately, since expect_error is built on top of the analyzer package, it means that a package cannot both depend on Flutter and expect_error at the same time.

As such, to use expect_error to test compilation errors when interacting with Flutter, a workaround is necessary.
The solution is to move your tests that depends on Flutter in a separate package used only for test purpose, with no dependency on Flutter.

As example, consider a package my_package that depends on Flutter, which exposes a MyWidget class:

// my_package/lib/my_widget.dart
import 'package:flutter/material.dart';

class MyWidget extends StatelessWidget {
  const MyWidget({Key? key, required String parameter}) : super(key: key);

  ...
}

To test this MyWidget class, rather than adding our test within the my_package/test folder, we could create a new Dart project such that our folder architecture looks like:

my_package
  puspec.yaml
  lib
    my_widget.dart
  expect_error_test
    pubspec.yaml
    test
      my_widget_test.dart

This expect_error_test app would depend on expect_error:

name: expect_error_test
...
dev_dependencies:
  expect_error: ...

Then, within expect_error_test/test/my_widget_test, we could do:

void main() {
  final flutterLibrary = await Library.custom(
    packageName: 'my_package', // the name of the package that contains this code block
    packageRoot: '..', // the path to the root of this package
    path: 'test/my_test.dart', // where the codeblock is located within the package
  );

  await expectLater(flutterLibrary.withCode(r'''
import 'package:my_package/my_widget.dart';

void main() {
  // expect-error: MISSING_REQUIRED_ARGUMENT
  MyWidget();
}
'''), compiles);
}

Notice how rather than Library.parseFromStacktrace() we used Library.custom(...).

By doing so, rather than assuming that our code block is within the same folder as our test, we were able to make it behave as if our code block was within my_package.

This way, our tests using expect_error are correctly able to import Flutter code.

You might also like...

A flutter utility to easily create flavors in your flutter application

Flutter Flavorizr A flutter utility to easily create flavors in your flutter application Getting Started Let's start by setting up our environment in

Jan 1, 2023

CLI utility to manage MC Server installations

CLI utility to manage MC server installations. Features Install required JDKs Download server files Generate start scripts (with optimized JVM flags)

Nov 18, 2022

Utility to process H264 profile-level-id values

h264_profile_level_id Dart utility to process H264 profile-level-id values based on Google's libwebrtc C++ code. API import 'package:h264_profile_leve

Apr 22, 2022

A utility library to automate mobile emulators.

emulators A utility library to automate mobile emulators. Can be used to automate screenshots on multiple devices. Example project https://github.com/

Nov 13, 2022

πŸ” πŸ‘€ CLI utility to check last-visit of your CodeForces friends & much more, πŸš€ powered by CodeForces API

JoJo πŸ” πŸ‘€ CLI utility to check last-visit of your CodeForces friends & much more, πŸš€ powered by CodeForces API Features Online Friends All Friends Pr

Jul 20, 2020

Minimal Dart wrapper to interact with Some Random Api. Easy to use, simplified and lightweight.

SRA - Some Random Api Minimal Dart wrapper to interact with Some Random Api. Easy to use, simplified and lightweight. Getting started Add the package

Jan 4, 2023

VS Code `.code-workspace` file generator

VS Code .code-workspace file generator (for monorepositories with Dart and Flutter projects) TL;DR; Create yaml file config.yaml (check #Format sectio

Feb 18, 2022

πŸš€The Flutter dart code generator from zeplin. ex) Container, Text, Color, TextStyle, ... - Save your time.

πŸš€The Flutter dart code generator from zeplin. ex) Container, Text, Color, TextStyle, ... - Save your time.

Flutter Gen Zeplin Extension πŸš€ The Flutter dart code generator from zeplin. ex) Container, Text, Color, TextStyle, ... - Save your time. ⬇ 1.1k Getti

Oct 12, 2022
Comments
  • fix: resolution of relative paths in `package_config.json`

    fix: resolution of relative paths in `package_config.json`

    PackageConfig.parseString should be given the URI of the package_config.json to parse, otherwise relative paths in package_config.json will be resolved into absolute paths incorrectly.

    This is blocking https://github.com/firebase/flutterfire/pull/10046.

    opened by blaugold 2
  • Bump analyzer from 4.7.0 to 5.0.0

    Bump analyzer from 4.7.0 to 5.0.0

    Bumps analyzer from 4.7.0 to 5.0.0.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 1
  • Bump analyzer from 3.4.1 to 4.0.0

    Bump analyzer from 3.4.1 to 4.0.0

    Bumps analyzer from 3.4.1 to 4.0.0.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 0
  • Bump analyzer from 2.8.0 to 3.4.1

    Bump analyzer from 2.8.0 to 3.4.1

    Bumps analyzer from 2.8.0 to 3.4.1.

    Changelog

    Sourced from analyzer's changelog.

    2.17.0

    Core libraries

    dart:core

    • Add Finalizer and WeakReference which can potentially detect when objects are "garbage collected".
    • Add isMimeType method to UriData class, to allow case-insensitive checking of the MIME type.
    • Add isCharset and isEncoding methods to UriData class, to allow case-insensitive and alternative-encoding-name aware checking of the MIME type "charset" parameter.
    • Make UriData.fromString and UriData.fromBytes recognize and omit a "text/plain" mimeType even if it is not all lower-case.

    dart:ffi

    • Add ref= and []= methods to the StructPointer and UnionPointer extensions. They copy a compound instance into a native memory region.
    • Add AbiSpecificIntegers for common C types:
      • char
      • unsigned char
      • signed char
      • short
      • unsigned short
      • int
      • unsigned int
      • long
      • unsigned long
      • long long
      • unsigned long long
      • uintptr_t
      • size_t
      • wchar_t
    • Add NativeFinalizer which can potentially detect when objects are "garbage collected". NativeFinalizers run native code where dart:core's Finalizers run Dart code on finalization.

    dart:html

    • Add scrollIntoViewIfNeeded to Element. Previously, this method was nested within scrollIntoView based on the ScrollAlignment value. scrollIntoView is unchanged for now, but users who intend to use the native Element.scrollIntoViewIfNeeded should use the new scrollIntoViewIfNeeded definition instead.

    • Change Performance.mark and Performance.measure to accept their different overloads. mark can now accept a markOptions map, and measure can now accept a startMark and endMark, or a measureOptions map. Both methods

    ... (truncated)

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 0
Owner
Remi Rousselet
Flutter enthusiast. You'll find me on stackoverflow. Or as a speaker in Flutter meetups
Remi Rousselet
Hot restart for Dart console application with fast incremental compilation.

Hot restart for Dart console application with fast incremental compilation. Why do I need this? If your console application compiles too long before r

Olzhas Suleimen 1 Oct 7, 2022
Quiver is a set of utility libraries for Dart that makes using many Dart libraries easier and more convenient, or adds additional functionality.

Quiver is a set of utility libraries for Dart that makes using many Dart libraries easier and more convenient, or adds additional functionality.

Google 905 Jan 2, 2023
null 2 Apr 17, 2022
A simple Flutter / Dart Utility class for converting complex objects to uri and query string

A simple Flutter / Dart Utility class for converting complex or nested objects to uri and query strings you can follow the the article on how this cla

Opata Joshua 5 Sep 7, 2022
Uproot(uprt) is a multi-platform (Windows, MacOs, and Linux) command line utility written in Dart to convert a router's DHCP IP Reservations between routers

UPROOT Uproot(uprt) is a multi-platform (Windows, MacOs, and Linux) command line utility written in Dart to convert a router's DHCP IP Reservations be

GeekVisit 73 Jan 1, 2023
A Pure Dart Utility library that checks for an Active Internet connection

This Code comes from https://github.com/komapeb/data_connection_checker * ?? Internet Connection Checker A Pure Dart Utility library that checks for a

Rounak Tadvi 61 Nov 25, 2022
Contains utility functions and classes in the style of dart:collection to make working with collections easier

The collection package for Dart contains a number of separate libraries with utility functions and classes that makes working with collections easier.

Dart 273 Dec 27, 2022
Auto is a Flutter automated testing framework developed for testers.

Auto Auto-A simpler Flutter UI automation test solution. No need to write any code Recording test scripts is very simple Mult

null 19 Oct 12, 2022
How to use the Robot Testing pattern in Flutter

Robot Testing Pattern in Flutter Developed with ?? by Very Good Ventures ?? This project showcases how to apply the Robot Testing pattern to a Flutter

Very Good Ventures 38 Dec 16, 2022
A flutter package with classes to help testing applications using the canvas

Canvas test helpers MockCanvas is a utility class for writing tests for canvas operations. It supports the same API as the regular Canvas class from d

Blue Fire 12 Jan 31, 2022