shouldly 0.0.2 shouldly: ^0.0.2 copied to clipboard
A simple, extensible, readable BDD assertion library.
shouldly #
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
only for now) - Custom matchers
Better test failure messages #
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 CustomNumberExtensions on Cap<num> {
Cap<num> get beNegative {
if (value >= 0) {
throw Exception('Value\n should be negative\n but was\n$value');
}
return Cap(value);
}
}
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 `num`', () {
10.should.beTypeOf<num>();
});
Strings #
test('should not start with substring', () {
const str = 'Flutter';
str.should.not.startWith('A');
});
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 target = {
'name': 'John',
'age': 18,
};
test('should contain key', () {
target.should.containKey('name');
});
test('should contain key with exact value', () {
target.should.containKeyWithValue('age', 18);
});
Functions #
test('should not throw throw an exception', () {
someMethod.should.notThrowException();
});
test('should throw exact type of exception', () {
throwExactException.should.throwExact<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),
),
);
});
More examples here
Writing Custom Matchers #
class Customer {
final bool isMarried;
final Gender gender;
Customer(this.isMarried, this.gender);
}
extension CustomerExtension on Customer {
Cap<Customer> get should => Cap<Customer>(this);
}
extension CustomerMatcherExtension on Cap<Customer> {
Cap<Customer> get beMarried {
if (!target.isMarried) {
throw Exception('target should be married');
}
return Cap(target);
}
Cap<Customer> get beMale {
if (isReversed) {
if (target.gender == Gender.male) {
throw Exception('target should be female');
}
} else {
if (target.gender != Gender.male) {
throw Exception('target should be male');
}
}
return Cap(target);
}
}
Contributing #
We accept the following contributions:
- Reporting issues
- Fixing bugs
- More tests
- Conjunction support (see: should.js and/or) (and conjunctions complete)
- More class integrations (Streams? Futures?)
- Improving documentation and comments