Search code examples
node.jsweb-servicesmocha.jschaiserver-sent-events

How to test restful webservices with Mocha and Chai


I'm new to writing unit tests and I'm trying to learn Mocha and Chai. In my Node+express project I've created a unit test like this:

import { expect } from 'chai';
var EventSource = require('eventsource');

describe('Connection tests', () => { // the tests container
    it('checks for connection', () => { // the single test
        var source = new EventSource('http://localhost:3000/api/v1/prenotazione?subscribe=300');
        source.onmessage = function(e: any) {
          expect(false).to.equal(true);
        };
    });
});

The http://localhost:3000/api/v1/prenotazione?subscribe=300 webservice is active when the test executes and I can see that Mocha does call it, because my webservice logs the incoming request. That webservice is using the SSE protocol and it never closes the connection, but it keeps sending data now and then over the same connection. EventSource is the client class that implements the SSE protocol, and it connects to the server when you set the onmessage callback into it. However Mocha does not wait for the webservice to return and the test passes whatever I write into expect function call. For example, only to debug the test code itself, I even wrote expect(false).to.equal(true); that obviously can never be true. However here is what I get when I run the test:

$ npm run test

> [email protected] test
> mocha -r ts-node/register test/**/*.ts --exit



  Connection tests
    ✔ checks for connection


  1 passing (23ms)

How do I make Mocha wait for the webservice to return data before resolving the test as passed?


Solution

  • After a few trials end errors, I found that

    1. When Mocha unit tests need to wait for something, they must return a Promise
    2. EventSource npm package (which is NOT 100% compatible with the native EventSource Javascript object), for some reason, maybe always, maybe only when used in Mocha or whatever, does not call the onmessage handler, so you have to add the event listener using the alternative addEventListener function

    Here is my working code:

    describe('SSE Protocol tests', () => {
        it('checks for notifications on data changes', function () { 
            this.timeout(0);
            return new Promise<boolean>((resolve, _reject) => {
              var eventSourceInitDict = {https: {rejectUnauthorized: false}};
              var source = new EventSource('http://localhost:3000/api/v1/prenotazione?subscribe=3600', eventSourceInitDict);
              var count = 2;
              source.addEventListener("results", function(event: any) {
                const data = JSON.parse(event.data);
                count--;
                if(count == 0)  {
                  resolve(true);
                }       
              });
            }).then(value => {
              assert.equal(typeof(value), 'boolean');
              assert.equal(value, true);
            }, error => {
              assert(false, error);
            });
        });
    });