使用Jest和ES模块测试客户端SSE

时间:2020-02-26 08:32:32

标签: javascript ecmascript-6 jestjs server-sent-events

目标是将与EventSource相关的功能放在一个类中,例如:

export default class EventSourceSetup {
    constructor() {
        let eventSource = new EventSource('http://localhost');

        eventSource.addEventListener('loading', function (event) {

        })

        eventSource.addEventListener('loaded', function (event) {

        })

        eventSource.addEventListener('error', function (event) {

        })

        eventSource.onerror = error => {
            console.error('EventSource failed: ', error);
        };
    }
}

应该模拟服务器,并且应该使用浏览器中带有windowEventSource的完整客户端功能。 并进行测试,例如:

import EventSourceSetup from './thumbnails'

describe('SSE', () => {
    beforeAll(() => {
    });

    it('first', () => {
        const eventSourceSetup = new EventSourceSetup();
    });
});

但是当我这样做时,我看到以下错误:

ReferenceError: EventSource is not defined

    at new EventSourceSetup (/Users/pharmosan/IdeaProjects/thumbnails-frontend/thumbnails.js:3:27)
    at Object.<anonymous> (/Users/pharmosan/IdeaProjects/thumbnails-frontend/thumbnails.test.js:8:34)
    at Object.asyncJestTest (/Users/pharmosan/IdeaProjects/thumbnails-frontend/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:100:37)
    at /Users/pharmosan/IdeaProjects/thumbnails-frontend/node_modules/jest-jasmine2/build/queueRunner.js:43:12
    at new Promise (<anonymous>)
    at mapper (/Users/pharmosan/IdeaProjects/thumbnails-frontend/node_modules/jest-jasmine2/build/queueRunner.js:26:19)
    at /Users/pharmosan/IdeaProjects/thumbnails-frontend/node_modules/jest-jasmine2/build/queueRunner.js:73:41
    at processTicksAndRejections (internal/process/task_queues.js:97:5)

1 个答案:

答案 0 :(得分:3)

这是单元测试解决方案:

index.js

export default class EventSourceSetup {
  eventSource;
  constructor() {
    let eventSource = new EventSource('http://localhost');
    this.eventSource = eventSource;

    eventSource.addEventListener('loading', function(event) {
      console.log('loading');
    });

    eventSource.addEventListener('loaded', function(event) {
      console.log('loaded');
    });

    eventSource.addEventListener('error', function(event) {
      console.log('error');
    });

    eventSource.onerror = (error) => {
      console.error('EventSource failed: ', error);
    };
  }
}

index.test.js

import EventSourceSetup from '.';

const mEventSourceInstance = {
  addEventListener: jest.fn(),
};
const mEventSource = jest.fn(() => mEventSourceInstance);

global.EventSource = mEventSource;

describe('SSE', () => {
  afterEach(() => {
    jest.clearAllMocks();
  });
  it('first', () => {
    mEventSourceInstance.addEventListener.mockImplementation((event, handler) => {
      if (event === 'loading' || event === 'loaded') {
        handler();
      }
    });
    const logSpy = jest.spyOn(console, 'log');
    new EventSourceSetup();
    expect(mEventSource).toBeCalledWith('http://localhost');
    expect(mEventSourceInstance.addEventListener).toBeCalledTimes(3);
    expect(logSpy).toBeCalledWith('loading');
    expect(logSpy).toBeCalledWith('loaded');
  });

  it('should handle error', () => {
    mEventSourceInstance.addEventListener.mockImplementation((event, handler) => {
      if (event === 'error') {
        handler();
      }
    });
    const logSpy = jest.spyOn(console, 'log');
    new EventSourceSetup();
    expect(mEventSource).toBeCalledWith('http://localhost');
    expect(mEventSourceInstance.addEventListener).toBeCalledTimes(3);
    expect(logSpy).toBeCalledWith('error');
  });

  it('should handle onerror', () => {
    const eventSourceSetup = new EventSourceSetup();
    const errorLogSpy = jest.spyOn(console, 'error');
    const mError = new Error('network');
    eventSourceSetup.eventSource.onerror(mError);
    expect(errorLogSpy).toBeCalledWith('EventSource failed: ', mError);
  });
});

单元测试结果覆盖率100%:

 PASS  stackoverflow/60409694/index.test.js (9.572s)
  SSE
    ✓ first (31ms)
    ✓ should handle error (1ms)
    ✓ should handle onerror (19ms)

  console.log node_modules/jest-mock/build/index.js:814
    loading

  console.log node_modules/jest-mock/build/index.js:814
    loaded

  console.log node_modules/jest-mock/build/index.js:814
    error

  console.log node_modules/jest-mock/build/index.js:814
    error

  console.error node_modules/jest-mock/build/index.js:814
    EventSource failed:  Error: network
        at Object.<anonymous> (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/60409694/index.test.js:44:20)
        at Object.asyncJestTest (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:100:37)
        at resolve (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/queueRunner.js:43:12)
        at new Promise (<anonymous>)
        at mapper (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/queueRunner.js:26:19)
        at promise.then (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/queueRunner.js:73:41)
        at process._tickCallback (internal/process/next_tick.js:68:7)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |                   
 index.js |     100 |      100 |     100 |     100 |                   
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        12.222s

jest.config.js

module.exports = {
  preset: 'ts-jest/presets/js-with-ts',
  testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
  verbose: true,
};