3.4.1 • Published 8 years ago

deprivation v3.4.1

Weekly downloads
4
License
Unlicense
Repository
github
Last release
8 years ago

Deprivation

This module facilitate whitebox and blackbox testing (binding it with conventional UT and MT paradigms) of nodejs applications.

We define a module as a folder with implementations.

These are the two main modes of operation of the deprivation module:

  • whitebox unit

    • grants a full access to an object, without the need to export everything in order to test it. useful e.g. in TDD (test small increments without exposing every method), and writing fine-grained tests.
  • blackbox module

    • gives the public access to an object

Both modes enable auto mocking.

Behind the curtains it uses the Node's VM module, and plows the require.cache.

Usage

npm install deprivation

For running a complete suite of tests use the npm test command.

Example implementation (Unit Under Test).

    var glob = require('glob');
    var dep = require('./dep.js');

    var myPrivateFunc = function(param){
        return glob.GlobSync(param);
    };

    var publicFunc = function(param) {
        return myPrivateFunc(param);
    };

    var callAnotherGlob = function() {
        return dep('huhu');
    };

    module.exports.publicFunc = publicFunc;

Basic

An example test file.

    var chamber = require("deprivation").chamber;
    var session = chamber("./implementation.js");

    // uut - Unit Under Test
    var uut = session.whitebox();

    uut.publicFunc("blabla"); // nothing special. Will call private func, which calls the original glob.GlobSync.
    uut.myPrivateFunc("blabla"); // However... note that this func is not exported, but still accessible in a test!
    uut.glob.GlobSync("blabla") // or even this...

Replace dependencies

It's possible to inject any type of a test double: mock, spy, stub, fake, etc., into the UUT.

Example dependency of UUT.

    // dep.js
    module.exports = require('glob').GlobSync;

Right after the module is 'loaded'

  • the UUT code is 'loaded' (= all the require statements are executed in the UUT)
  • the dependencies are replaced after exposition of the UUT
  • replacement in not transitive!
// let's get rid of glob.GlobSync dependency
    uut.glob.GlobSync = function(){};

// all calls execute the dummy function
    uut.publicFunc('blabla');
    uut.myPrivateFunc('blabla');
    uut.glob.GlobSync('blabla');

// ...but not this one!
    uut.callAnotherGlob();

Through an option

Leads to a different result:

  • if the replacement is an object, the require initialization code of the replaced dependancies is not executed
  • if the replacement is a string (as in the require statement), the require initialization code is executed
  • replacement is transitive (it is replaced globally)
    var myGlob = {GlobSync: function() {return './.ssh/id_rsa.priv'}}
    var session = chamber('./implementation.js', {replace:[{'glob': myGlob}]});
    var uut = session.whitebox();

    // all calls return './.ssh/id_rsa.priv'
    uut.glob.GlobSync('something');
    uut.callAnotherGlob('something');

Blackbox, through an option, with more automation

If a function exists, which accepts an object, and returns its test double,

// A jasmine spy-maker example

    var myReplacer = function (obj) {
        Object.keys(obj).forEach(function (item) {
            spyOn(obj, item);
        });
    };

it can be passed on with the replacer option.

    seance = chamber("myModule/impl.js", {replace: ['glob', '../*'], replacer: myReplacer});

In the above example

  • the magical '../*' string means that all implementations outside of myModule folder will be automatically transformed into spies. This omits the node_module folder.
  • due to the above, the glob package is added explicitly, and will be automatically turned into a mock,

    '*' replaces all implementations (within the same folder too!)

An example test suite (jasmine/mocha):

    beforeEach(function () {
        sut = seance.blackbox();
        spies = seance.getTestDoubles();
    });

spies above are the spy objects references, stored in a dictionary. This allows to work with objects, that are inaccessible from the module's public interface.

The expectation may be set, using the obtained references.

    it('uses GlobSync', function () {
        sut.arrangeHeapDumps('bleble');
        expect(spies['node_modules/glob/glob.js'].GlobSync).toHaveBeenCalled();
    });

Test doubles are accessed using the path relative to the process current directory. This is the most readable way to specify, which test double object is referenced (the glob package may be used by other sub-packages, in different versions, etc.)

Clone the project from the repository and refer to the test/*.* files for more examples.

3.4.1

8 years ago

3.3.1

8 years ago

3.3.0

8 years ago

3.2.1

8 years ago

3.2.0

8 years ago

3.1.11

8 years ago

3.1.10

8 years ago

3.1.9

8 years ago

3.1.8

8 years ago

3.1.7

8 years ago

3.1.6

8 years ago

3.1.5

8 years ago

3.1.1

8 years ago

3.1.0

8 years ago

3.0.1

8 years ago

3.0.0

8 years ago

2.0.5

8 years ago

2.0.4

8 years ago

2.0.3

8 years ago

2.0.2

8 years ago

2.0.1

8 years ago

2.0.0

8 years ago

1.2.0

8 years ago

1.1.2

8 years ago

1.1.1

8 years ago

1.0.3

8 years ago

1.0.2

8 years ago

1.0.1

8 years ago

1.0.0

8 years ago