0

Aside from the probable dozens of bugs you can spot in the code below, I'd really like to know what would most people would consider testing in the code below. I have 8 similar exported functions, so I believe it is those that I should be testing.

The problem is, they all call the private function sendTokBoxMessage so I can't test that function is being called. Moving it to it's own module seems overkill as this is already a small contained module and adding complexity for the sake of testing feels wrong to me.

That being the case, I thought I should test that the http request that sendTokBoxMessage makes happens. But this calls another private function getSessionIdForRoom so then I need to stub the roomStoreModel. I have to do the same for various other dependencies (moment, apiKey, apiSecret) and then use a mock http framework to spy on the request.

Do you think it is good practice to setup all the above, or is the opinion that if the module is small and simple enough, setting up tests is more effort than it's worth?

Thanks.

'use strict';
let request = require('request');
let moment = require('moment');
let jwt = require('jwt-simple');
let logger = require('../business/log_business');
let OpenTokConnection = require('../controllers/opentokConnection');
let ot = OpenTokConnection.ot;
let apiKey = OpenTokConnection.apiKey;
let apiSecret = OpenTokConnection.apiSecret;
let roomStoreModel = require('../model/roomstore_model')(ot, apiKey, apiSecret);

var getSessionIdForRoom = function(roomName) {
  return roomStoreModel.getSessionIdForRoom(roomName);
};

var createWebToken = function() {
  let now = moment().unix();
  let expires = now + 180;
  let claims = {'iss': apiKey, 'ist': 'project', 'iat': now, 'exp': expires, 'jti': 'jwt_corp'};
  let token = jwt.encode(claims, OpenTokConnection.apiSecret);

  return token;
};

var sendTokBoxMessage = function(roomName, endpoint, data) {
  getSessionIdForRoom(roomName).then(function(sessionId) {
    //Get the web token
    var webToken = createWebToken();

    //Set the headers
    var headers = {
      'X-OPENTOK-AUTH': webToken,
      'Content-Type': 'application/json'
    };

    // Configure the request
    var options = {
      url: 'https://api.opentok.com/v2/project/' + apiKey + '/session/' + sessionId + '/signal',
      method: 'POST',
      headers: headers,
      json: {'type': endpoint, 'data': JSON.stringify(data)},
      forever: true
    };

    // Start the request
    request(options, function(error, response) {
      if (error || response.statusCode !== 204) {
        logger.log('couldnt send tokbox message to endpoint, probably no one left to send to: ' + endpoint, roomName);
      }
    });
  });
};

exports.sendLogMessage = function(roomName, endPoint, data) {
  sendTokBoxMessage(roomName, endPoint, data);
};

exports.sendMessageToEveryone = function(roomName, endPoint, data) {
  sendTokBoxMessage(roomName, endPoint, data);
  logger.log('Sending message to everyone: ' + endPoint, 1, roomName);
};

exports.sendRestartRoomMessage = function(roomName) {
  sendTokBoxMessage(roomName, 'restartRoom', {'restart': 'restart'});
  logger.log('Sending restart room to everyone', 1, roomName);
};

exports.sendUserUpdateMessage = function(roomName, user, action) {
  var data = {'action': action, 'user': user};

  sendTokBoxMessage(roomName, 'userUpdated', data);
  logger.log('Sending userUpdated room to everyone: ' + user.uid, 1, roomName);
};

exports.sendRoomStateUpdateMessage = function(roomName, roomState) {
  sendTokBoxMessage(roomName, 'roomStateUpdate', roomState);
  logger.log('Sending roomState update to everyone: ' + JSON.stringify(roomState), 1, roomName);
};

exports.sendChatMessage = function(roomName, message) {
  sendTokBoxMessage(roomName, 'chatMessage', message);
  logger.log('Sending chat message: ' + JSON.stringify(message), 1, roomName);
};

exports.sendZipDxConnectedMessage = function(roomName) {
  sendTokBoxMessage(roomName, 'zipdxConnected', {'null': 'null'});
  logger.log('Sending ZipDx connected message', 1, roomName);
};

exports.sendZipDxDisconnectedMessage = function(roomName) {
  sendTokBoxMessage(roomName, 'zipdxDisconnected', {'null': 'null'});
  logger.log('Sending ZipDx disconnected message', 1, roomName);
};
1
  • Given that the end point of all of those functions is an HTTP call, which you certainly don't want to make for real in a unit test, I'd recommend using something like nock to test that.
    – jonrsharpe
    Aug 4 '17 at 9:51
4

You can only test code which is properly modularized. Proper unit testing always requires some concessions in your software architecture. But those usually tend to improve your code by making it more modular and flexible.

The createWebToken should be tested to create a correct claims object. Unfortunately that's impossible with the way it is currently written, because 1. it depends on moment().unix(); which it gets by itself and 2. the claims-object never leaves the function. In order to make it testable, you would have to extract the creation of the claims object into a separate function which takes the current time as input. That function would then be perfectly testable.

Similar the sendTokBoxMessage should be split into two functions. One function createToxBoxRequest which returns a request for a given roomSessionId, endpoint and data. That way you can test that a correct request is created for each possible input. The actual function sentToxBoxRequest would then take that ToxBoxRequest object and send it.

What remains are a bunch of functions which are nothing more than a thin wrapper for sending an XmlHttpRequest. There is little meaning in testing these with a unit test, because they rely 99.99% on a network component. But if you want to test those, you need to mock the system the messages get send to.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.