Как проверить маршрут Server Sent Events (SSE) в NodeJS? - PullRequest
7 голосов
/ 27 января 2020

В моем приложении NodeJS есть маршрут Server Sent Events, на который клиенты могут подписаться для получения обновлений в режиме реального времени с сервера. Это выглядит следующим образом:

router.get('/updates', (req, res) => {
    res.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive'
    })

    const triggered = (info) => {
        res.write(`\ndata: ${JSON.stringify(info)}\n\n`)
    }

    eventEmitter.addListener(constants.events.TRIGGERED, triggered)

    req.on('close', () => {
        eventEmitter.removeListener(constants.events.TRIGGERED, triggered)
    })
})

Тестирование традиционного маршрута с использованием supertest достаточно просто в узле:

test('Should get and render view', async() => {
    const res = await request(app)
        .get('/')
        .expect(200)

    expect(res.text).not.toBeUndefined()
})

Однако это не работает при тестировании маршрута SSE.

У кого-нибудь есть идеи, как протестировать SSE-маршрут с Node? Это не обязательно должно быть проверено с supertest. Просто ищу идеи о том, как его протестировать, supertest или как-то иначе.

РЕДАКТИРОВАТЬ: У меня есть идея о том, как это проверить интеграции. По сути, перед тестом нужно будет раскрутить сервер, подписаться на него во время теста и закрыть его после теста. Тем не менее, он не работает должным образом в Jest, когда я использую beforeEach () и afterEach () для ускорения сервера.

1 Ответ

3 голосов
/ 06 февраля 2020

Я бы смоделировал / фальсифицировал все, что используется конечной точкой, и проверил, выполняется ли конечная точка в правильном порядке с правильными переменными. Во-первых, я объявил бы функцию trigger и функцию обратного вызова close вне конечной точки, чтобы я мог проверить их напрямую. Во-вторых, я бы исключил все глобальные ссылки во всех функциях в пользу параметров функции:

let triggered = (res) => (info) => {
    res.write(`\ndata: ${JSON.stringify(info)}\n\n`);
}

let onCloseHandler = (eventEmitter, constants, triggered, res) => () => {
    eventEmitter.removeListener(constants.events.TRIGGERED, triggered(res));
}

let updatesHandler = (eventEmitter, constants, triggered) => (req, res) => {
    res.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive'
    });

    eventEmitter.addListener(constants.events.TRIGGERED, triggered(res));

    req.on('close', onCloseHandler(eventEmitter, constants, triggered, res));
};

router.get('/updates', updatesHandler(eventEmitter, constants, triggered));

С этим кодом тестовые случаи будут выглядеть так:

test("triggered", () => {
    let res;

    beforeEach(() => {
        res = generateFakeRespone();
    });

    it("should execute res.write with the correct variable", () => {
        trigger(res)("whatever");

        expect(res.write).to.have.been.called.once;
        expect(res.write).to.have.been.called.with(`\ndata: ${JSON.stringify("whatever")}\n\n`);
    });
});


test("onCloseHandler", () => {
    let res;
    let eventEmitter;
    let constants;
    let triggered;

    beforeEach(() => {
        res = Math.random();
        eventEmitter = generateFakeEventEmitter();
        constants = generateFakeConstants();
        triggered = generateFakeTriggered();
    });

    it("should execute eventEmitter.removeListener", () => {
        onCloseHandler(eventEmitter, constants, triggered, res);

        expect(eventEmitter.removeListener).to.have.been.called.once;
        expect(eventEmitter.removeListener).to.have.been.called.with(/*...*/)
    });
});

test("updatesHandler", () => {
    beforeEach(() => {
        req = generateFakeRequest();
        res = generateFakeRespone();
        eventEmitter = generateFakeEventEmitter();
        constants = generateFakeConstants();
        triggered = generateFakeTriggered();
    });

    it("should execute res.writeHead", () => {
        updatesHandler(eventEmitter, constants, triggered)(req, res);

        expect(res.writeHead).to.have.been.called.once;
        expect(res.writeHead).to.have.been.called.with(/*...*/)
    });

    it("should execute req.on", () => {
        //...
    });

    // more tests ...
});

При таком стиле кодирование и тестирование, у вас есть возможность сделать очень подробный модульный тест. Недостатком является то, что для правильного тестирования требуется гораздо больше усилий.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...