shouldly 0.4.1 copy "shouldly: ^0.4.1" to clipboard
shouldly: ^0.4.1 copied to clipboard

outdated

A simple, extensible BDD assertion library which focuses on giving great error messages when the assertion fails.

shouldly logo

pub package codecov likes style: lint Dart

shouldly is an assertion concept that focuses on

  • writing assertions as plain English sentences
  • and giving better error messages when the assertion fails while being simple and terse.

Inspired from Fluent Assertion, Shouldly, should.js

Features #

  • More readable test code
  • Better test failure messages
  • Conjunction support (and)
  • Custom assertions

Readability #

More readable test code as plain English sentence.

// without shouldly
expect(playerCharacter.health, 100);

// shouldly
playerCharacter.health.should.be(100);

No more: Mix Up with parameters #

You can mix up with Expected or Actual 🤔. But with shouldly there is no way to mix up.

// without shouldly
expect(playerCharacter.health, 100);
expect(100, playerCharacter.health);

// shouldly
playerCharacter.health.should.be(100);

No more: single heap of assertion methods #

Every single type of class has his own assertions.

Better test failure messages #

To get more contextual information

drawing

drawing

drawing

Conjunctions #

This is a real English sentence, is it not?

13.should.beOdd().and.beGreaterOrEqualThan(13);

participants.should.contain('Andrew').and.not.contain('Bobby');

Custom matchers #

extension CustomNumAssertions on NumericAssertions {
  NumericAssertions get beNegative {
    if (subject >= 0) {
      throw ShouldlyTestFailureError('Number\n  should be negative');
    }
    return NumericAssertions(subject);
  }
}

Or more exotic matchers #

test('Custom matchers', () {
  final bobby = Customer(
    isMarried: true,
    gender: Gender.male,
  );
  bobby.should.beMale.and.beMarried;

  final kate = Customer(
    isMarried: true,
    gender: Gender.female,
  );
  kate.should.beMarried.and.not.beMale;
});

Getting started #

Simple add shouldly dependency into your project.

dev_dependencies:
  shouldly: <latest>

Usage #

Objects #

Every single object has following assertion methods:

Method Example Failure message
be 1.should.be(2); Expected int should be 2 but was 1
beOfType 2.0.should.beOfType<double>();
beAssignableTo 3.should.beAssignableTo<int>();
beNull null.should.beNull();
beOneOf 5.should.beOneOf([1, 2, 5]);
test('should be not null', () {
  final obj = Object();
  obj.should.not.beNull();
});

test('should be null', () {
  const Object? obj = null;
  obj.should.beNull();
});

test('should be type of `int`', () {
  const obj = 1;
  obj.should.beOfType<int>();
});

test('should be assignable to `num`', () {
  const obj = 1;
  obj.should.beAssignableTo<num>();
});

Booleans #

Method Example Failure message
beTrue false.should.beTrue(); Target boolean should be True but was False
beFalse false.should.not.beFalse(); Target boolean should not be False but was False
test('false should be `false`', () {
  false.should.beFalse();
});

test('false should not be `true`', () {
  false.should.not.beTrue();
});

Numbers #

Method Example Failure message
bePositive (-1).should.bePositive(); Target int should be positive but was negative as -1
beNegative 1.should.beNegative(); Target int should be negative but was positive as 1
beZero 10.should.beZero(); Target int should be 0 but was 10
beOdd 8.should.beOdd(); Target int should be odd but was even as 8
beEven 7.should.beEven(); Target int should be event but was odd as 7
beGreaterThan 1.should.beGreaterThan(2); Target int should be greater than 2 but does not
beAbove 3.should.beAbove(2);
beLessThan 3.should.beLessThan(4);
beBelow 3.should.beBelow(4);
beGreaterOrEqualThan 3.should.beGreaterOrEqualThan(3);
beLessOrEqualThan 3.should.beLessOrEqualThan(3);
beWithin 3.should.beWithin(1,5);
beCloseTo pi.should.beCloseTo(3.14, delta: 0.01);
beTolerantOf pi.should.beTolerantOf(3.14, tolerance: 1%);
test('Int should be type of `int`', () {
  2.should.beEven();
  10.should.beGreaterThan(9);
  9.99.should.not.beCloseTo(10.0, delta: 0.01);
});

Strings #

Method Example Failure message
startWith 'Flutter'.should.startWith('f');
endWith 'Flutter'.should.endWith('a');
haveLength 'Flutter'.should.haveLength(10);
beNullOrEmpty 'Flutter'.should.beNullOrEmpty();
beNullOrWhiteSpace 'Flutter'.should.beNullOrWhiteSpace();
beBlank 'Flutter'.should.beBlank();
match 'Flutter'.should.match('*a');
contain 'Flutter'.should.contain('a');
test('should not start with substring', () {
  'Flutter'.should.not.startWith('A');
});

DateTimes #

Method Example Failure message
beCloseTo DateTime.now().should.beCloseTo(Date(2025, 1, 1));
beAfter DateTime.now().should.beCloseTo(Date(2022, 1, 1));
beBefore DateTime.now().should.beBefore(Date(2222, 1, 1));
// before
DateTime(2021, 9, 9).should.beBefore(DateTime(2021, 9, 10));
DateTime(2021, 9, 9).should.not.beBefore(DateTime(2021, 9, 9));

// close to
DateTime(2021, 9, 9, 1, 1, 1, 2).should.beCloseTo(
      DateTime(2021, 9, 9, 1, 1, 1, 3),
      delta: Duration(milliseconds: 1),
    );

Iterables #

Method Example Failure message
haveCount [1, 2, 3].should.haveCount(7);
contain [1, 2, 3].should.contain(7);
containAll [1, 2, 3].should.containAll([7, 8]);
every [1, 2, 3].should.every((x) => x > 10);
any [1, 2, 3].should.any((x) => x == 0);
test('should contain', () {
  [1, 200, 3].should.contain(200);
});

test('should not contain', () {
  [1, 2, 4].should.not.contain(3);
});

test('with every element in collection is true for predicate', () {
  [3, 5, 7, 9].should.every((item) => item < 10);
});

test('with some elements in collection is true for predicate', () {
  [3, 5, 7, 9].should.any((item) => item > 8);
});

Maps #

Method Example Failure message
haveCount {}.should.haveCount(7);
beEmpty {}.should.beEmpty();
containKey {'name': 'Bobby'}.should.containKey('age');
containKeys {}.should.containKeys(['age', 'name']);
haveValueInKey {}.should.haveValueInKey('name');
containKeyWithValue {}.should.containKeyWithValue('name', 'Bobby');
contain {}.should.haveValueInKey(<MapEntry>[]);
final subject = {
  'name': 'John',
  'age': 18,
};

test('should contain key', () {
  subject.should.containKey('name');
});

test('should contain key with exact value', () {
  subject.should.containKeyWithValue('age', 18);
});

Functions #

Method Example Failure message
throwException Should.throwException(() => myFunc());
notThrowException Should.notThrowException(() => myFunc());
throwError Should.throwError(() => myFunc());
notThrowError Should.notThrowError(() => myFunc());
completeIn Should.completeIn(Duration(seconds: 1), () => myFunc());
Should.throwException(() => someMethodWitchThrowException(params:));
Should.throwError<ExactError>(() => someMethodWitchThrowExactError(params:));
test('async function should throw exception', () async {
  await Should.throwAsync(() {
    Future.delayed(Duration(milliseconds: 100));
    throw Exception('test');
  });
});

test('async function should throw exact exception', () async {
  await Should.throwAsync<CustomException>(() {
    Future.delayed(Duration(milliseconds: 100));
    throw CustomException('custom exception test');
  });
});

test('should complete in a duration', () async {
  await Should.completeIn(
    Duration(seconds: 1),
    func: () => slowFunction(
      Duration(milliseconds: 900),
    ),
  );
});

Enums #

test('should not be equal', () {
  seasons.spring.should.not.be(seasons.winter);
});

test('should not be type of', () {
  seasons.spring.should.not.beOfType<level>();
});

test('should be assignable to `Enum`', () {
  seasons.spring.should.beAssignableTo<Enum>();
});

More examples here

Writing Custom Matchers #

extension CustomerExtension on Customer {
  CustomerAssertions get should => CustomerAssertions(this);
}

class CustomerAssertions extends BaseAssertions<Customer, CustomerAssertions> {
  CustomerAssertions(
    Customer? subject, {
    bool isReversed = false,
    String? subjectLabel,
  }) : super(subject, isReversed: isReversed, subjectLabel: subjectLabel);

  CustomerAssertions get beMarried {
    if (isReversed) {
      if (subject!.isMarried) {
        throw ShouldlyTestFailure('Customer should not be married');
      }
    } else {
      if (!subject!.isMarried) {
        throw ShouldlyTestFailure('Customer should be married');
      }
    }
    return CustomerAssertions(subject);
  }

  CustomerAssertions get beMale {
    if (isReversed) {
      if (subject!.gender == Gender.male) {
        throw ShouldlyTestFailure('Customer should be female');
      }
    } else {
      if (subject!.gender != Gender.male) {
        throw ShouldlyTestFailure('Customer should be male');
      }
    }

    return CustomerAssertions(subject);
  }

  @override
  CustomerAssertions copy(
    Customer? subject, {
    bool isReversed = false,
    String? subjectLabel,
  }) =>
      CustomerAssertions(
        subject,
        isReversed: isReversed,
        subjectLabel: subjectLabel,
      );
}

Recommendations #

You can improve the readability of the rest of your test code with given_when_then_unit_test, which enhances the test report readability as well.

drawing

Changelog #

Please see the Changelog page to know what's recently changed.

Contributing #

Feel free to contribute to this project.

If you find a bug or want a feature, but don't know how to fix/implement it, please fill an issue.
If you fixed a bug or implemented a new feature, please send a pull request.

We accept the following contributions:

  • Ideas how to improve
  • Reporting issues
  • Fixing bugs
  • More tests
  • More class integrations (Functions, Futures, Functions)
  • Improving documentation and comments

Maintainers #

Support me

23
likes
0
pub points
64%
popularity

Publisher

verified publisherdevcraft.ninja

A simple, extensible BDD assertion library which focuses on giving great error messages when the assertion fails.

Repository (GitHub)
View/report issues

License

unknown (LICENSE)

Dependencies

collection

More

Packages that depend on shouldly