Search code examples
node.jsunit-testingmocha.jssinon

NodeJS unit testing


Possibly a naive question but I have a class similar to(mine has more data read from database and caching them):

var genericClass = class {

    constructor() {
        this.genericStructure = {};
    }

    async getState(key) {
        //some more core logic
        return this.genericStructure[key];
    }

    async putState(key, genericJson) {
        this.genericStructure[key] = genericJson;
        return true;
    }
}

its getState and putState are used multiple times in a consumer class, I want to mock all these occurences from a local map object in my test class. Can it be done?

I am using chai, mocha, sinon

"devDependencies": {
        "chai": "^4.1.2",
        "chai-as-promised": "^7.1.1",
        "eslint": "^4.19.1",
        "mocha": "^5.2.0",
        "nyc": "^12.0.2",
        "sinon": "^6.0.0",
        "sinon-chai": "^3.2.0"
    }

Solution

  • Instead of letting your class initialize the structure it wants, you should provide (inject) it the the structure on initialization. This is the principle of Dependency Injection ( Constructor injection ).

    var genericClass = class {
    
        constructor(initialStructure) {
            this.genericStructure = initialStructure;
        }
    
        async getState(key) {
            //some more core logic
            return this.genericStructure[key];
        }
    
        async putState(key, genericJson) {
            this.genericStructure[key] = genericJson;
            return true;
        }
    }
    

    And now in your tests you can pass whatever you like for initialStructure.

    Updated Answer

    From your comment seems like the question was a little confusing. I think what you need is to mock the class methods as well to change their functionality, and at the same time support dependency injection. You can use something like below -

    function testClassProxy(map) {
    
        let handleSetProps = function(args){
          map.set(args[0], args[1]);
          return true;
        }
    
        let handleGetProps = function(args){
          return map.get(args[0]);
        }
    
        let handler = {
            get(target, propKey) {
                return function (...args) {
                    switch(propKey){
                      case 'setProps':{
                        return handleSetProps(args)
                      } 
                      case 'getProps': {
                        return handleGetProps(args)
                      }
                    }
                };
            }
        };
    
        return new Proxy(map, handler);
    }
    
    
    let proxy = testClassProxy(new Map());
    
    
    console.log(
      proxy.setProps('a', '1'),
      proxy.getProps('a')
    );