juiceTest<TBloc extends JuiceBloc<TState>, TState extends BlocState> function

  1. @isTest
void juiceTest<TBloc extends JuiceBloc<TState>, TState extends BlocState>(
  1. String description, {
  2. required TBloc build(),
  3. TState seed()?,
  4. FutureOr<void> act(
    1. TBloc bloc
    )?,
  5. Duration? wait,
  6. int skip = 0,
  7. Object expect()?,
  8. Object errors()?,
  9. FutureOr<void> verify(
    1. TBloc bloc
    )?,
  10. bool allowInFlightAtClose = false,
  11. Object? tags,
  12. Object? skipTest,
})

Declares a bloc test: build, (seed), act, then — with the bloc CLOSED — assert on exactly what it emitted.

juiceTest<CounterBloc, CounterState>(
  'increment emits the new count on the counter group',
  build: () => CounterBloc(),
  act: (bloc) => bloc.send(IncrementEvent()),   // return the future
  expect: () => [
    isUpdatingStatus(
      state: predicate<CounterState>((s) => s.count == 1),
      groups: {CounterGroups.counter},
    ),
  ],
);

No fixed sleeps: send() returns a future that completes when the event is fully processed, so act returning it is enough. Use wait only for work that outlives processing on purpose (a debounce, a timer).

  • seed puts the bloc in a starting state (via the built-in UpdateEvent) before recording starts; the seed emission is not recorded.
  • expect returns the expected emissions, one matcher (or value) per emission, in order — use isUpdatingStatus, isWaitingStatus, isFailureStatus, isCancelingStatus. skip drops that many leading emissions first.
  • errors returns the expected use-case errors (the use_case_error spans, in order). Without it, any use-case error fails the test.
  • verify runs last, with the closed bloc, for further checks.
  • The bloc is closed BEFORE asserting. The test fails if a use case was still running when the bloc closed (a telemetry START without its END — typically an act that didn't return the send() future), or if anything emitted during the close. Opt out with allowInFlightAtClose: true. Fire-and-forget work a use case left behind (a bare timer) can't be observed; its emit is dropped by the close fence.

Implementation

@isTest
void juiceTest<TBloc extends JuiceBloc<TState>, TState extends BlocState>(
  String description, {
  required TBloc Function() build,
  TState Function()? seed,
  FutureOr<void> Function(TBloc bloc)? act,
  Duration? wait,
  int skip = 0,
  Object Function()? expect,
  Object Function()? errors,
  FutureOr<void> Function(TBloc bloc)? verify,
  bool allowInFlightAtClose = false,
  Object? tags,
  Object? skipTest,
}) {
  scaffolding.test(
    description,
    () => runJuiceTest<TBloc, TState>(
      build: build,
      seed: seed,
      act: act,
      wait: wait,
      skip: skip,
      expect: expect,
      errors: errors,
      verify: verify,
      allowInFlightAtClose: allowInFlightAtClose,
    ),
    tags: tags,
    skip: skipTest,
  );
}