Last updated:
0 purchases
given when then unit test
given_when_then_unit_test #
A Flutter package for creating more readable tests. If you are not familiar with Flutter's Unit tests
Given we feel that our tests are the best documentation of the behaviors in our code.
When we read our tests.
Then we want them to be easy to understand.
And test code be elegant.
But be written without any pain.
Features #
Improve test code readability #
// Without `given_when_then`
group('calculator', () {
// ...
group('add 1', () => calc.add(1), then: () {
test('result should be 1', () {
// ...
});
group('[and] subtract 1', () => calc.subtract(1), body: () {
test('res should be 0', () {
// ...
});
});
});
});
// 🔥 With `given_when_then` as a common English sentence
given('calculator', () {
// ...
when('add 1', () => calc.add(1), then: () {
then('result should be 1', () {
// ...
});
when('[and] subtract 1', () => calc.subtract(1), body: () {
then('res should be 0', () {
// ...
});
});
});
});
copied to clipboard
With shouldly it makes super readable test code 😍
given('calculator', () {
late Calculator calc;
before(() {
calc = Calculator();
});
when('add 1', () {
before(() => calc.add(1));
then('result should be 1', () {
calc.res.should.be(1);
});
when('[and] subtract 1', () {
before(() => calc.subtract(1));
then('res should be 0', () {
calc.res.should.beZero();
});
});
});
});
copied to clipboard
Auto compose test message as BDD style #
Without given_when_then
✓ calculator When add 1 result should be 1
✓ calculator When add 1 [and] subtract 1 res should be 0
copied to clipboard
With given_when_then with minimal effort
✓ Given empty calculator When add 1 Then result should be 1
✓ Given empty calculator When add 1 and subtract 1 Then res should be 0
copied to clipboard
Usage #
Simple example #
Without given_when_then
group('empty calculator', body: () {
late Calculator calc;
setUp(() {
calc = Calculator();
});
group('add 1', () {
setUp(() {
calc.add(1);
});
test('result should be 1', () {
calc.res.should.be(1);
});
group('[and] subtract 1', () {
setUp(() {
calc.subtract(1);
});
test('res should be 0', () {
calc.res.should.beZero();
});
});
});
});
copied to clipboard
With given_when_then
given('empty calculator', () {
late Calculator calc;
before(() {
calc = Calculator();
});
when('add 1', () => calc.add(1), then: () {
then('result should be 1', () {
calc.res.should.be(1);
});
and('subtract 1', () => calc.subtract(1), body: () {
then('res should be 0', () {
calc.res.should.beZero();
});
});
});
});
copied to clipboard
Advanced example with mocking #
given('Post Controller', body: () {
late PostController postController;
late IPostRepository mockPostRepository;
late IToastr mockToastr;
before(() {
mockPostRepository = MockPostRepository();
mockToastr = MockToastr();
postController = PostController(
repo: mockPostRepository,
toastr: mockToastr,
);
});
whenn('save new valid post', () {
bool? saveResult;
before(() async {
when(() => mockPostRepository.addNew('new post'))
.thenAnswer((_) => Future.value(true));
saveResult = await postController.addNew('new post');
});
then('should return true', () async {
saveResult.should.beTrue();
});
then('toastr shows success', () async {
verify(() => mockToastr.success('ok')).called(1);
});
});
whenn('save new invalid post', () {
bool? saveResult;
before(() async {
when(() => mockPostRepository.addNew('new invalid post'))
.thenAnswer((_) => Future.value(false));
saveResult = await postController.addNew('new invalid post');
});
then('should return false', () async {
saveResult.should.beFalse();
});
then('toastr shows error', () async {
verify(() => mockToastr.error('invalid post')).called(1);
});
});
});
copied to clipboard
Test cases #
There are two ways how to use test cases:
version 1
testCases([
const TestCase([1, 1, 2]),
const TestCase([5, 3, 8])
], (testCase) {
final x = testCase.args[0] as int;
final y = testCase.args[1] as int;
given('two numbers $x and $y', () {
//
when('summarizing them', () {
then('the result should be equal to ${testCase.args.last}', () {
(x + y).should.be(testCase.args[2] as int);
});
});
});
});
copied to clipboard
✓
Given two numbers 1 and 1
When summarizing them
Then the result should be equal to 2
✓
Given two numbers 5 and 3
When summarizing them
Then the result should be equal to 8
copied to clipboard
version 2 - with generic
testCases2<String, String>([
const TestCase2('Flutter', 'F'),
const TestCase2('Awesome', 'A'),
], (args) {
test('Word ${args.arg1} start with ${args.arg2}', () {
args.arg1.should.startWith(args.arg2);
});
});
copied to clipboard
✓ Word Flutter start with F
✓ Word Awesome start with A
copied to clipboard
Formatting the test report 📜 #
You can format the test report, make it in a single line, or print every step on each line by setting variable GivenWhenThenOptions.pads with any integer value, e.g.
GivenWhenThenOptions.pads = 4;
copied to clipboard
and result will be:
Given the account balance is $100
And the card is valid
And the machine contains enough money
When the Account Holder requests $20
Then the Cashpoint should dispense
And the account balance should be $80
And the card should be returned
copied to clipboard
Known Issues #
Collision with mocktail or mockito packages which bring where method too, you can hide when and use whenn of this package like below
But prefer to hide and rename imports like so.
import 'package:mocktail/mocktail.dart' hide when;
import 'package:mocktail/mocktail.dart' as mktl show when;
copied to clipboard
import 'package:given_when_then_unit_test/given_when_then_unit_test.dart' hide when;
void main() {
given('Post Controller', () {
// .. omit
whenn('save new invalid post', () {
// ... omit
then('should return false', () async {
saveResult.should.beFalse();
});
then('toastr shows error', () async {
verify(() => mockToastr.error('invalid post')).called(1);
});
});
});
}
copied to clipboard
And & But #
Steps And and But are inter-changeable.
However, But in the English language is generally used in a negative context. And using But makes the intent of the test explicit and removes any possible ambiguities.
Contributing #
We accept the following contributions:
Ideas how to improve readability or performance
Reporting issues
Fixing bugs
Improving documentation and comments
Maintainers #
Andrew Piterov
For personal and professional use. You cannot resell or redistribute these repositories in their original state.
There are no reviews.