matewilk
matewilk

Reputation: 1301

return Readable mock from needle using nock - twitter filtered stream test

I've been trying for a few hours now and I'm still struggling how to properly test my connection to twitter filtered stream.

This is how my class I want to test looks like:

import { injectable } from 'inversify';
import { ReadableStream } from 'needle';
import needle from 'needle';

@injectable()
export class TwitterStreamAdapter {
  public stream: ReadableStream | undefined;

  public startStream = (): void => {
    const streamUrl = process.env.TWITTER_STREAM_URL as string;
    this.stream = needle.get(streamUrl, { // <<----- I WANT TO TEST THIS 
      headers: { Authorization: `Bearer ${process.env.TWITTER_BEARER_TOKEN}` },
    });

    this.stream
      .on('data', (data) => {
        try {
          console.log(JSON.parse(data));
        } catch (e) {
          // Keep alive signal received. Do nothing.
        }
      })
  };
}

In short, here I'm creating a connection to the twitter filtered stream api, which is kept alive at all times and it's a stream to which tweets are being send.

First, I want to test, if I'm getting a proper stream from the needle.get although you might probably argue I'm trying to test the library instead of my implementation.

Bear with me for a minute and see the test below:

  beforeEach(() => (twitterStreamAdapter = new TwitterStreamAdapter()));

  it('connects to filtered stream and returns stream', (done) => {
    const host = 'https://api.twitter.com';
    const path = '/2/tweets/search/stream';
    const mockedStream = new PassThrough(); // <<---- I want to be able to emit with this stream
    const scope = nock(host, {
      reqheaders: {
        authorization: `Bearer ${process.env.TWITTER_BEARER_TOKEN}`,
      },
    })
      .get(path)
      .delay(2000)
      .reply(200, () => mockedStream);

    twitterStreamAdapter.startStream();

    twitterStreamAdapter.stream!.emit('data', { message: 'hello world'}); // <<--- this works, but I don't want to use the prop of the instance to emit

    mockedStream.emit('data', 'hello world 2'); // <<---- this doesn't work, I'd like to use mockedStream to emit

    expect(twitterStreamAdapter.stream).toEqual(mockedStream); // <<--- this expectation fails
  });

Here in the test, I'd like to be able to create a mockedStream, and I'd like this stream to be returned from needle.get. This way I would have more control over my tests. First of all, I wouldn't have to make my class property this.stream public.

In the test, You can see that I'm doing twitterStreamAdapter.stream.emit which works and emits the event which is being passed to the callback of this.stream.on('data' ...) which you can see in the classes' method implementation.

Also in the test, there is this line:

expect(twitterStreamAdapter.stream).toEqual(mockedStream);

How do I actually can test if the proper stream (the mocked one) is returned from needle.get.

I'm a bit confused about all of this to be honest, it's the first time I'm trying to test a request that actually stays alive and returns streams of data.

Any help greatly appreciated!

EDIT ---- based on what I've researched so far ----- but still have issues:

So far I've rewritten my current test code to this:

it('handles stream data event', async (done) => {
    const mockedResponse = { message: 'hello from nock' };

    nock(host, {
      reqheaders: {
        authorization: `Bearer ${process.env.TWITTER_BEARER_TOKEN}`,
      },
    })
      .get(path)
      .reply(200, () => mockedResponse);

    twitterStreamAdapter.startStream();
    const stream: ReadableStream | undefined = twitterStreamAdapter.stream;

    jest.spyOn(JSON, 'parse');

    stream!.on('data', () => {
      expect(JSON.parse).toHaveBeenCalledWith({ message: 'hello from nock' });
      done();
    });
  });

Here, I'm successfully getting data event triggered and the test works, The problem is when I try to trigger (emit) the error event.

I tried:

nock(...).get(path).replyWithError({code: 'ETIMEDOUT'});

and also:

nock(...).get(path).reply(500);

And it doesn't emit the error event.

Upvotes: 0

Views: 743

Answers (0)

Related Questions