Spec Testing Framework

repository·main·Indexed 43 days ago

https://github.com/invertase/spec

A streamlined, type-safe testing framework for Dart and Flutter. It provides a declarative API for writing tests with improved IDE autocompletion and a Jest-inspired CLI tool featuring interactive watch mode, parallel execution, and CI integration via a GitHub Action.

Tokens
3.1K
Snippets
14
Records
18
Agent score
54%

What's inside Spec

  1. Overview of Spec testing framework

    main
    Spec is a streamlined testing framework for Dart and Flutter that builds on top of existing testing tools. It provides a more declarative, type-safe, and elegant testing environment designed to reduce errors and improve IDE autocompletion. Spec is available as both a CLI tool for running tests and a Dart package for writing them.
  2. Overview of Spec

    main
    Spec is a streamlined testing framework for Dart and Flutter. It builds on top of existing Dart and Flutter testing tools to provide a more elegant testing environment. Spec is distributed as both a CLI tool for running tests and a Dart package for writing them.
  3. Use the Spec CLI to run tests

    main

    The Spec CLI provides an intuitive testing output interface inspired by Jest. Key features include:

    • Interactive Mode: Use spec --watch to automatically re-run tests when source code changes and to specifically rerun only failed tests.
    • Parallel Execution: Run both Dart and Flutter tests in parallel with a single command.
    • Multi-package Support: Run tests from multiple packages simultaneously using Melos.

    To run your tests, use the spec command in your terminal.

    spec --watch
  4. Write type-safe tests with the Spec package

    main

    The spec package provides a declarative, type-safe API for writing tests, designed to improve IDE autocompletion and reduce errors compared to standard Dart testing syntax.

    Comparison

    Standard Dart testing (not type-safe):

    expect(value, equals(42));
    await expectLater(Future.value(42), completion(equals(42)));

    With Spec (type-safe):

    expect(value).toEqual(42);
    await expect(Future.value(42)).completion.toEqual(42);
    test('dart test example', () async {
      int value = 42;
    
      // Without spec - not type safe
      expect(value, equals(42));
      await expectLater(Future.value(42), completion(equals(42)));
    
      // With spec - type safe
      expect(value).toEqual(42);
      await expect(Future.value(42)).completion.toEqual(42);
    });
  5. Use Spec matchers for testing Futures, Streams, and Functions

    main

    The spec package provides declarative, type-safe matchers that extend standard Dart test matchers. These are designed to improve readability and IDE autocompletion.

    Testing Futures

    Use .completion to assert the value resolved by a Future.

    Testing Streams

    Use .emits to assert values, nullity, or errors emitted by a Stream.

    Testing Functions

    Use .returnsNormally() or .throws to assert the behavior of a function call.

    import 'package:spec/spec.dart';
    
    void main() {
      test('future example', () async {
        final future = Future.value(42);
        expect(future).toEqual(future);
        await expect(future).completion.toEqual(42);
        await expect(future).throws.isArgumentError();
      });
    
      test('stream example', () async {
        final stream = Stream.value(42);
        await expect(stream).emits.toEqual(42);
        await expect(stream).emits.isNull();
        await expect(stream).emits.not.isNull();
        await expect(stream).emitsError.isArgumentError();
      });
    
      test('function example', () {
        void throwsFn() => throw Error();
        expect(throwsFn).returnsNormally();
        expect(throwsFn).throws.isArgumentError();
      });
    }
  6. Interactive commands in `spec` watch mode

    main

    When running in --watch mode, the CLI becomes interactive. You can use specific key presses to control the test runner:

    Watch Mode Controls

    KeyAction
    qStop the watch mode.
    wToggle the tooltip explaining available commands.
    fToggle "run failing tests only" mode.
    tStart editing the test name filter.
    gToggle --update-goldens mode.
    EnterIf tests are running: Stop them. If tests are idle: Restart them.

    Test Name Filter Editing

    When in the test name filter editing mode (triggered by t):

    • Characters: Type to append to the filter string.
    • Enter: Submit the filter and restart tests. |
    • Escape: Abort the edit and return to watch mode. |
    • Delete: Remove the last character from the filter string. |
  7. Use Spec CLI interactive watch mode

    main

    Spec supports an interactive watch mode that automatically re-runs tests when source code changes. You can trigger manual runs or filter tests using keyboard shortcuts.

    To start watch mode, use:

    spec --watch

    Watch Mode Shortcuts:

    • f: Run only failed tests.
    • t: Filter by a test name regex pattern.
    • Enter: Trigger a test run.
    • q: Quit watch mode.
  8. Assert that a Future does not complete

    main

    The doesNotComplete() method allows you to match a Future that is expected to remain pending and never complete.

    Warning: This creates an asynchronous expectation. The expect() call containing this matcher will return immediately, and execution will continue without waiting for the future to resolve or fail.

    expect(myFuture).doesNotComplete();
  9. Assert that a function returns normally

    main

    Use the returnsNormally() extension method on an Expectation<Fn> to assert that a function call does not throw any exceptions. The function will be called exactly once, and any exceptions thrown will be swallowed by the matcher.

    Note: The function being tested must not take arguments. If you need to test a function with arguments, wrap it in an anonymous function (closure) first.

    // Example: testing a function that takes no arguments
    expect(() => myFunc(), returnsNormally);
    
    // Example: testing a function with arguments using a wrapper
    expect(() => myFuncWithArgs(1, 2), returnsNormally);