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

outdated

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

shouldly #

pub package codecov likes style: lint Dart

Shouldly is an assertion framework which focuses on giving great error messages when the assertion fails while being simple and terse.

shouldly allows you write more readable test assertions.

Features #

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

Better test failure messages #

To get more contextual information

// Non-Shouldly
Expected: <18>
  Actual: <17>

// Shouldly
Subject int should be
  `18`
but was
  `17`

// more with Shouldly 😍
customer.age should be
  `18`
but was
  `17`
// Non-Shouldly
Expected: not null
  Actual: <null>

// Shouldly
Subject should not be null
// Non-Shouldly
Expected: some element <4>
  Actual: [1, 2, 3, 5]

// Non-Shouldly
myList
    [1, 2, 3, 5]
should contain
    4
but does not

Readability #

More readable test code as an English sentence.

// without shouldly
expect(calculator.currentValue, 1);

// shouldly
calculator.currentValue.should.be(1);

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);

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.

Usage #

Booleans #

test('false should be `false`', () {
  false.should.beFalse();
});

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

Numbers #

test('Int should be type of `int`', () {
  10.should.beOfType<int>();
  10.should.beAssignableTo<num>();
});

Strings #

test('should not start with substring', () {
  const str = 'Flutter';
  str.should.not.startWith('A');
});

DateTimes #

// 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 #

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 #

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 #

test('should throw exact type of exception', () {
  throwExactException.should.throwException<CustomException>();
});

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),
    ),
  );
});

Objects #

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>();
});

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,
      );
}

Contributing #

We accept the following contributions:

  • Reporting issues
  • Fixing bugs
  • More tests
  • More class integrations (Streams? Futures?)
  • Improving documentation and comments

Maintainers #

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