Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support testing of emit.forEach #4206

Open
haf opened this issue Jul 17, 2024 · 5 comments
Open

feat: support testing of emit.forEach #4206

haf opened this issue Jul 17, 2024 · 5 comments
Labels
needs repro info The issue is missing a reproduction sample and/or steps question Further information is requested waiting for response Waiting for follow up

Comments

@haf
Copy link

haf commented Jul 17, 2024

Description

I'm trying to test how the state changes based on a stream that's listened to via emit.ForEach. However, the stream returned from the process operation is never listened to in the test:

    blocTest(
      'Adding media triggers pipeline events',
      wait: Duration.zero,
      setUp: () async {
        testMedia = await testPhoto(
          'Image.heic',
          basePath: path.normalize(path.join(pwd(), '../../../core/media/test-data')),
        );

        // Simulate pipeline events
        final pipelineEvents = [
          PipelineSuccess(media: testMedia),
        ];

        when(pipeline.process(any)).thenAnswer((_) => Stream.fromIterable(pipelineEvents));
      },
      build: () => bloc,
      act: (EditListingsBloc bloc) => bloc.add(MediaAdded(listingId: testListingId, media: testMedia, isFirstMedia: true)),
      expect: () => [
        // this fails since the stream is never read
        predicate<EditListingsState>((state) {
          return state.listings[testListingId]?.medias?.isEmpty ?? false;
        }),
      ],
    );

Desired Solution

In a test situation, I'd expect testBloc to await all open emitters (the programmer should take care to ensure they close / complete).

Alternatives Considered

I've tried to understand the mechanism by which this does not work; and it would seem that bloc.close() in the testing frameworks, cancels the broadcast stream before it's even consumed.

@felangel
Copy link
Owner

Hi @haf 👋
Thanks for opening an issue!

You can take a look at the Flutter Todos Example for a reference. If that doesn't help, then feel free to share a link to a minimal reproduction sample and I'm happy to look and open a PR with suggestions.

@felangel felangel added question Further information is requested waiting for response Waiting for follow up needs repro info The issue is missing a reproduction sample and/or steps labels Jul 18, 2024
@haf
Copy link
Author

haf commented Jul 18, 2024

So I'm asking as if:

() => todosRepository.saveTodo(
mockTodos.first.copyWith(isCompleted: true),
),
).called(1);
returned a stream — I know it's being called and that's not what I want to test. Makes sense? does use forEach, but it doesn't do bloc.add inside a stream listening callback — which is what I'd like to test.

Let's start by understanding each other before either of us invests into building any examples or features :) I'd love to answer any questions and/or discuss as needed!

@haf
Copy link
Author

haf commented Jul 22, 2024

Hope you've had a great weekend! Do you have any further questions that might be clarifying?

@felangel
Copy link
Owner

Hey @haf, thanks! Hope you had a great weekend as well!

Can you share a snippet of the event handler you want to test? I’m happy to help you write a test 👍

@haf
Copy link
Author

haf commented Jul 23, 2024

Sure; so from one handler I have this code:

  // media
  Future<void> _onMediaAdded(MediaAdded event, Emitter<EditListingsState> emit) async {
    // things here... nextState = state.copyWith ...;
    emit(nextState);

    await emit.forEach(
      pipeline.process(event.media),
      onData: (pe) {
        add(ForwardedPipelineEvent(event: pe));
        return state;
      },
    );
  }

// then later


  Future<void> _onForwardedPipelineEvent(ForwardedPipelineEvent forwarded, Emitter<EditListingsState> emit) async {
     switch (forwarded.event) {
      case final BranchSuccess bsuc:
        // the pro tags should be displayed
        if (bsuc.branchName != proTag) {
          break;
        }
        // nextState = ...

        break;

      case final PipelineSuccess _:
        // Mark the corresponding media as uploaded in the state
        // nextState = ...
        break;

      default:
        break;
    }

    emit(nextState);

    // If the pipeline was successful, save the listing, as we probably have updated it with the new media
    if (forwarded.event is PipelineSuccess) {
      add(Save(listingId: lid, reason: 'onPipelineEvent(PipelineSuccess)'));
    }
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs repro info The issue is missing a reproduction sample and/or steps question Further information is requested waiting for response Waiting for follow up
Projects
None yet
Development

No branches or pull requests

2 participants