testController<C extends BaseController<State> , State> function
- @isTest
Creates a new controller
-specific test case with the given description
.
testController will handle asserting that the controller
emits the expect
ed
states (in order) after act
is executed.
testController also handles ensuring that no additional states are emitted
by closing the controller
stream before evaluating the expect
ation.
build
should be used for all controller
initialization and preparation
and must return the controller
under test.
seed
is an optional Function
that returns a state
which will be used to seed the controller
before act
is called.
act
is an optional callback which will be invoked with the controller
under
test and should be used to interact with the controller
.
skip
is an optional int
which can be used to skip any number of states.
skip
defaults to 0.
wait
is an optional Duration
which can be used to wait for
async operations within the controller
under test such as debounceTime
.
expect
is an optional Function
that returns a Matcher
which the controller
under test is expected to emit after act
is executed.
verify
is an optional callback which is invoked after expect
and can be used for additional verification/assertions.
verify
is called with the controller
returned by build
.
errors
is an optional Function
that returns a Matcher
which the controller
under test is expected to throw after act
is executed.
testController(
'CounterController emits [1] when increment is added',
build: () => CounterController(),
act: (controller) => controller.add(CounterEvent.increment),
expect: () => [1],
);
testController can optionally be used with a seeded state.
testController(
'CounterController emits [10] when seeded with 9',
build: () => CounterController(),
seed: () => 9,
act: (controller) => controller.add(CounterEvent.increment),
expect: () => [10],
);
testController can also be used to skip
any number of emitted states
before asserting against the expected states.
skip
defaults to 0.
testController(
'CounterController emits [2] when increment is added twice',
build: () => CounterController(),
act: (controller) {
controller
..add(CounterEvent.increment)
..add(CounterEvent.increment);
},
skip: 1,
expect: () => [2],
);
testController can also be used to wait for async operations
by optionally providing a Duration
to wait
.
testController(
'CounterController emits [1] when increment is added',
build: () => CounterController(),
act: (controller) => controller.add(CounterEvent.increment),
wait: const Duration(milliseconds: 300),
expect: () => [1],
);
testController can also be used to verify
internal controller functionality.
testController(
'CounterController emits [1] when increment is added',
build: () => CounterController(),
act: (controller) => controller.add(CounterEvent.increment),
expect: () => [1],
verify: (_) {
verify(() => repository.someMethod(any())).called(1);
}
);
Note: when using testController with state classes which don't override
==
and hashCode
you can provide an Iterable
of matchers instead of
explicit state instances.
testController(
'emits [StateB] when EventB is added',
build: () => MyController(),
act: (controller) => controller.add(EventB()),
expect: () => [isA<StateB>()],
);
Implementation
@isTest
void testController<C extends BaseController<State>, State>(
String description, {
required C Function() build,
State Function()? seed,
Function(C controller)? act,
Duration? wait,
int skip = 0,
dynamic Function()? expect,
Function(C controller)? verify,
dynamic Function()? errors,
}) {
test.test(description, () async {
await internalControllerTest<C, State>(
build: build,
seed: seed,
act: act,
wait: wait,
skip: skip,
expect: expect,
verify: verify,
errors: errors,
);
});
}