Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing of private functions with Mocha and Node.js

I am using Mocha in order to unit test an application written for Node.js.

I wonder if it's possible to unit test functions that have not been exported in a module.

Example:

I have a lot of functions defined like this in foobar.js:

function private_foobar1(){
    ...
}

function private_foobar2(){
    ...
}

And a few functions exported as public:

exports.public_foobar3 = function(){
    ...
}

The test case is structured as follows:

describe("private_foobar1", function() {
    it("should do stuff", function(done) {
        var stuff = foobar.private_foobar1(filter);
        should(stuff).be.ok;
        should(stuff).....

Obviously this does not work, since private_foobar1 is not exported.

What is the correct way to unit-test private methods? Does Mocha have some built-in methods for doing that?

like image 771
fstab Avatar asked Feb 28 '14 14:02

fstab


People also ask

How do you test private methods in Mocha?

To test private functions in unit tests with Mocha and Node. js, we can use the rewire module. const rewire = require("rewire"); const foobar = rewire("./foobar"); describe("private_foobar1", () => { const privateFoobar1 = foobar.

Can private functions be unit tested?

Unit Tests Should Only Test Public Methods The short answer is that you shouldn't test private methods directly, but only their effects on the public methods that call them. Unit tests are clients of the object under test, much like the other classes in the code that are dependent on the object.

Is Mocha used for unit testing?

For your NodeJS applications, Mocha and Chai can be used together for Unit Testing.


9 Answers

Check out the rewire module. It allows you to get (and manipulate) private variables and functions within a module.

So in your case the usage would be something like:

var rewire = require('rewire'),
    foobar = rewire('./foobar'); // Bring your module in with rewire

describe("private_foobar1", function() {

    // Use the special '__get__' accessor to get your private function.
    var private_foobar1 = foobar.__get__('private_foobar1');

    it("should do stuff", function(done) {
        var stuff = private_foobar1(filter);
        should(stuff).be.ok;
        should(stuff).....
like image 73
barwin Avatar answered Oct 18 '22 22:10

barwin


If the function is not exported by the module, it cannot be called by test code outside the module. That's due to how JavaScript works, and Mocha cannot by itself circumvent this.

In the few instances where I determined that testing a private function is the right thing to do, I've set some environment variable that my module checks to determine whether it is running in a test setup or not. If it runs in the test setup, then it exports additional functions that I can then call during testing.

The word "environment" is loosely used here. It might mean checking process.env or something else that can communicate to the module "you're being tested now". The instances where I've had to do this were in a RequireJS environment, and I've used module.config for this purpose.

like image 30
Louis Avatar answered Oct 18 '22 23:10

Louis


Here is a really good workflow to test your private methods explained by Philip Walton, a Google engineer on his blog.

Principle

  • Write your code normally
  • Bind your private methods to the object in a separate code block, and mark it by an _ (for example)
  • Surround that code block by start and end comments

Then use a build task or your own build system (for example grunt-strip-code) to strip this block for production builds.

Your tests builds have access to your private API, and your production builds have not.

Snippet

Write your code as this:

var myModule = (function() {

  function foo() {
    // Private function `foo` inside closure
    return "foo"
  }

  var api = {
    bar: function() {
      // Public function `bar` returned from closure
      return "bar"
    }
  }

  /* test-code */
  api._foo = foo
  /* end-test-code */

  return api
}())

And your Grunt tasks like this:

grunt.registerTask("test", [
  "concat",
  "jshint",
  "jasmine"
])
grunt.registerTask("deploy", [
  "concat",
  "strip-code",
  "jshint",
  "uglify"
])

Deeper

In a later article, it explains the "why" of "testing private methods"

like image 23
Rémi Becheras Avatar answered Oct 18 '22 23:10

Rémi Becheras


If you'd prefer to keep it simple, just export the private members as well, but clearly separated from the public API with some convention, e.g. prefix them with an _ or nest them under a single private object.

var privateWorker = function() {
    return 1
}

var doSomething = function() {
    return privateWorker()
}

module.exports = {
    doSomething: doSomething,
    _privateWorker: privateWorker
}
like image 32
famousgarkin Avatar answered Oct 18 '22 22:10

famousgarkin


I have added an extra function that I name Internal() and return all private functions from there. This Internal() function is then exported. Example:

function Internal () {
  return { Private_Function1, Private_Function2, Private_Function2}
}

// Exports --------------------------
module.exports = { PublicFunction1, PublicFunction2, Internal }

You can call the internal functions like this:

let test = require('.....')
test.Internal().Private_Function1()

I like this solution best because:

  • only one function Internal() is always exported. This Internal() function is always used to test private functions.
  • It is simple to implement
  • Low impact on the production code (only one extra function)
like image 41
Perez Lamed van Niekerk Avatar answered Oct 18 '22 23:10

Perez Lamed van Niekerk


I made an npm package for this purpose that you might find useful: require-from

Basically, you expose non-public methods by:

module.testExports = {
    private_foobar1: private_foobar1,
    private_foobar2: private_foobar2,
    ...
}

Note: testExports can be any valid name you want, except exports of course.

And from another module:

var requireFrom = require('require-from');
var private_foobar1 = requireFrom('testExports', './path-to-module').private_foobar1;
like image 21
DEADB17 Avatar answered Oct 18 '22 23:10

DEADB17


I know that this is not necessarily the answer you are looking for, but I have found that most of the time if a private function is worth testing, it's worth being in its own file.

E.g., instead of having private methods in the same file as the public ones, like this...

src/thing/PublicInterface.js


function helper1 (x) {
    return 2 * x;
}

function helper2 (x) {
    return 3 * x;
}

export function publicMethod1(x) {
    return helper1(x);
}

export function publicMethod2(x) {
    return helper1(x) + helper2(x);
}

...you split it up like this:

src/thing/PublicInterface.js

import {helper1} from './internal/helper1.js';
import {helper2} from './internal/helper2.js';

export function publicMethod1(x) {
    return helper1(x);
}

export function publicMethod2(x) {
    return helper1(x) + helper2(x);
}

src/thing/internal/helper1.js

export function helper1 (x) {
    return 2 * x;
}

src/thing/internal/helper2.js

export function helper2 (x) {
    return 3 * x;
}

That way, you can easily test helper1 and helper2 as-is, without using Rewire and other "magic" (which, I have found, have their own pain points while debugging, or when you try to make your move towards TypeScript, not to mention poorer understandability for new colleagues). And them being in a sub-folder called internal, or something like that, will help avoiding accidental usage of them in unintended places.


P.S.: Another common issue with "private" methods is that if you want to test publicMethod1 and publicMethod2 and mock the helpers, again, you normally need something like Rewire to do that. However, if they are in separate files, you can use Proxyquire to do it, which, unlike Rewire, doesn't need any changes to your build process, is easy to read and to debug, and works well even with TypeScript.

like image 32
Dániel Kis-Nagy Avatar answered Oct 18 '22 23:10

Dániel Kis-Nagy


To make private methods available for testing, I do this:

const _myPrivateMethod: () => {};

const methods = {
    myPublicMethod1: () => {},
    myPublicMethod2: () => {},
}

if (process.env.NODE_ENV === 'test') {
    methods._myPrivateMethod = _myPrivateMethod;
}

module.exports = methods;
like image 26
MFB Avatar answered Oct 18 '22 23:10

MFB


I followed barwin's answer and checked how unit tests can be made with rewire module. I can confirm that this solution simply works.

The module should be required in two parts - a public one and a private one. For public functions you can do that in standard way:

const { public_foobar3 } = require('./foobar');

For private scope:

const privateFoobar = require('rewire')('./foobar');
const private_foobar1 = privateFoobar .__get__('private_foobar1');
const private_foobar2 = privateFoobar .__get__('private_foobar2');

In order to know more about the subject, I created a working example with full module testing, testing includes private and public scope.

For further information I encourage you to check the article (How to test private functions of a CommonJS module) fully describing the subject. It includes code samples.

like image 42
Maciej Sikora Avatar answered Oct 19 '22 00:10

Maciej Sikora