TestCaseCombinator<T> class
Builder for test cases based on all possible combinations of the input arguments.
Very useful for the cases when you need test cases covering all possible input parameter combinations.
Example:
We need to test the following code:
enum Food {
apple,
banana,
yoghurt,
}
class Monkey {
final bool stomachFull;
const Monkey({
this.stomachFull = false,
});
bool shouldEat(Food food) {
if (stomachFull) return false;
return food == Food.banana;
}
}
Specifically, we're interested in shouldEat
functionality.
Based on the logic we can see in shouldEat
, we can determine that if monkey is
given some food, it will only eat banana, but it will reject even a banana
if it is already full.
Given that, we can determine that one condition is boolean (stomachFull
), that
can take 2 values - true
and false
.
Another condition is Food
, which has 3 options - apple
, banana
, and yoghurt
.
This gives us 6 combinations total that we have to check. From those combinations we only have 1 case when monkey will eat the food - if it's stomach is not full and it's given a banana.
This is a perfect example when writing all cases by hand is a lot of time and effort, complex readability, and a significant probability of introducing errors.
Instead, we can use TestCaseCombinator to make all the combinations for us:
final _monkeyTestCases = TestCaseCombinator<({bool stomachFull, Food food})>(
(stomachFull: false, food: Food.apple),
[
([true, false], (input, value) => (stomachFull: value, food: input.food)),
(Food.values, (input, value) => (stomachFull: input.stomachFull, food: value)),
],
)
..successfulCase((stomachFull: false, food: Food.banana));
Now we can write our test:
void main() {
group('Verify monkey eating food', () {
for (var testCase in _monkeyTestCases.testCases) {
test(testCase.description, () {
final monkey = Monkey(stomachFull: testCase.input.stomachFull);
expect(monkey.shouldEat(testCase.input.food), testCase.isSuccessful,
reason: testCase.isSuccessful
? 'Monkey is expected to eat ${testCase.input.food.name} when it is ${testCase.input.stomachFull ? 'full' : 'hungry'}'
: 'Monkey should not eat ${testCase.input.food.name} when it is ${testCase.input.stomachFull ? 'full' : 'hungry'}');
});
}
});
}
The test goes over all testCases, created by TestCaseCombinator and sets up a test for each one,
where each test case provides a record with a particular input, in our case a record containing the stomach flag
with some food, a description of the test case itself (example: true | Food.apple
), and isSuccessful
flag, indicating
if the test case is considered successful (from the test perspective).
The only thing we had to do is add the actual expectation to our test.
The test will be called with the following parameters:
stomachFull | food | isSuccessful |
---|---|---|
true | Food.apple | false |
true | Food.banana | false |
true | Food.yoghurt | false |
false | Food.apple | false |
false | Food.banana | true |
false | Food.yoghurt | false |
That provided a pretty comprehensive testing of shouldEat
method, covering all possible cases.
Now we can be sure the monkey is always eating healthy!
Constructors
-
TestCaseCombinator(T initialValue, List<
TestCaseCombinatorArgument< arguments)T> > -
Creates the combinator with
initialValue
which will be used for taking all possible combinations ofarguments
.
Properties
- hashCode → int
-
The hash code for this object.
no setterinherited
- runtimeType → Type
-
A representation of the runtime type of the object.
no setterinherited
-
testCases
→ Iterable<
({String description, T input, bool isSuccessful})> -
Provides all test cases where each test case provides the
input
,description
, andisSuccessful
flag, indicating if this test case is expected to be a success.no setter
Methods
-
noSuchMethod(
Invocation invocation) → dynamic -
Invoked when a nonexistent method or property is accessed.
inherited
-
successfulCase(
T testCase) → void -
Provides a
testCase
which indicates a successful case (for the test). -
successfulCaseIf(
bool comparator(T)) → void - Runs a comparator against all testCases and adds matching cases as successful.
-
toString(
) → String -
A string representation of this object.
inherited
Operators
-
operator ==(
Object other) → bool -
The equality operator.
inherited