mfncooper / mockery Goto Github PK
View Code? Open in Web Editor NEWSimplifying the use of mocks with Node.js
License: Other
Simplifying the use of mocks with Node.js
License: Other
Below code does not look work at all,
I do not think I've made any mistakes here, but console still logs out the return from from original aws-sdk
require call, not newly mocked {}
test.spec.js
require('jest');
const mockery = require('mockery');
describe('test', () => {
beforeAll(() => {
mockery.enable({
warnOnReplace: false,
warnOnUnregistered: false
});
mockery.registerMock('aws-sdk', {});
const aws = require('aws-sdk');
console.log(aws);
});
it('', () => {
expect(true).toBeTruthy();
});
});
strangely, this does work if
Hi all,
I'm very new to unit testing, and I'm trying to get familiar with these mock libraries.
I tried to not use the enable function when setup and only do registerMock for some packages, but it's not working for me. I'm guessing the enable function is a must, so is there anyway to just register mock for some module instead of intercepting all of them? I also tried rewire, but apparently it can't intercept any require calls.
Hi Martin,
I don't think this is necessarily an issue with Mockery, but I'm having a hard time finding a good solution for this problem. The problem is that if I run one test that requires a real module, and then run another that mocks that same module, the mocked version fails (the reverse is also seems to be true but I'm having a hard time proving it). This is only a problem when a test runner loads all the test files at once, which caches the require paths and mockery gets bypassed (or something).
I've simplified the problem into a standalone git repo here:
https://github.com/BryanDonovan/nodejs-mock-test
If you have a chance to take a look, I'd really appreciate it. It might just be something obvious that I'm doing wrong.
Thanks,
Bryan
ERROR in ./~/mockery/mockery.js
Module not found: Error: Cannot resolve module 'module' in /private/var/www/coders51/mta-components/node_modules/mockery
@ ./~/mockery/mockery.js 39:8-25
Any ideas? Thanks!
Hi, I know this has been covered a bit before but I don't seem to be able to do this in a reasonable manner.
What I would like is this.
I have a mut ( module under test ). It takes a dependency on gesPromise. I mock gesPromise with gesPromiseMock. the module is a function which takes args my mock is a function that takes setup args then return a function the stub function. So here is my mock and how i use it.
module.exports = function (result){
return function(conn, name, skipTake) {
var data = JSON.stringify({eventName: "someEvent"});
var newVar = !result ? {
Status: 'OK',
NextEventNumber:3,
Events: [{Event:{EventName:'someEvent',Data: data}},{Event:{EventName:'someEvent',Data: data}},{Event:{EventName:'someEvent',Data: data}}],
IsEndOfStream: false
} : result;
return Promise.resolve(newVar);
}
};
var result = {
Status: 'StreamNotFound',
NextEventNumber: 500,
Events: [{}],
IsEndOfStream: false
};
mockery.registerMock('./readStreamEventsForwardPromise', readStreamEventsForwardPromiseMock(result))
so what I would like is to be able to change the "results" for different tests. The only way I've been able to do that so far is with the following set. Note that I must use completely different describe setups.
describe('getEventStoreRepository', function() {
var mut;
before(function(){
mockery.enable({
warnOnReplace: false,
warnOnUnregistered: false
});
var result = {
Status: 'StreamNotFound',
NextEventNumber: 500,
Events: [{}],
IsEndOfStream: false
};
mockery.registerAllowable('../src/ges/gesRepository', true);
mockery.registerMock('ges-client', connection);
mockery.registerMock('./gesPromise', {readStreamEventsForwardPromise: readStreamEventsForwardPromiseMock(result), appendToStreamPromise:appendToStreamPromiseMock} );
});
describe('#getById_return bad results', function() {
context('when calling getById with proper args but stream deleted', function (){
it('should throw proper error', function () {
mut = require('../src/ges/gesRepository')();
var id = uuid.v1();
var streamName = streamNameStrategy(TestAgg.aggregateName(),id);
var byId = mut.getById(TestAgg, id, 0);
return byId.must.reject.error(Error, 'Aggregate Deleted: '+streamName);
})
});
});
after(function () {
mockery.deregisterAll();
mockery.disable();
});
});
now if I do another setup for a test exactly like the above but with different result data it will work.
this is a lot of ceremony for each test. Is there any way to do it for each test rather than a whole setup?
thanks,
r
sorry about all the implementation details.
Now all modules will be cached in this new clean cache, until you later disable Mockery again. The new cache is temporary, and is discarded when Mockery is disabled.
When loading a module via require
, its reference is also pushed to the array under children
property of the module where the require
function was used.
Even after mockery swaps back the old cache and deletes the reference to the new temporary cache there are still references to all loaded modules in parents Module.children
array so the memory used by those modules can't be freed.
There is a missing feature, that when I use useCleanCache: true
, I can't specify a file/folder to be excluded from the cleaning, and to remain as a chached module.
The problems is this:
I have a file that creates an in-memory database with bookshelf.js (and a few options setting the connection is to a sqlite db). If I use useCleanCache: true
, that file will be "unloaded" and when I require it again in my tests, it will be loaded again.
The result of this, will be that I will have an unlodaded in-memory database and a new in-memory database.
Notes:
I know there is a discussion if I should or shouldn't test with a db or not. I know it's not the best practice to do it, because it won't be a real unit test, but I would like to in this case. Also, setting up a real db for testing would make the testing slower. I don't want to enter in this discusion right now :D
The solution:
Add a new option: remainingCache: [RegExp]
which will contain an array of RegExps, and will be used only if useCleanCache: true
. This will change the override of m._cache = {}
with an object filled with the original modules that the file name matched any of the RegExps.
I will make a PR with this if you are ok!
Maybe it's worth mentioning in the readme or wiki that when used with Jasmine mockery needs to be enclosed in a define () and the 'module' should be received as an argument.
The var m = require('module')
is simply not working. Maybe it's a Jasmine issue or an undocumented expected behavior.
define(['module'], function (m) {
//var m = require('module') // this call fails even after enclosing it in a define(['module']) {}
.
.
.
return { ... };
}
using Yadda together mockery has some unexpected side effects.
# goes wrong
Yadda = require 'yadda'
mockery.enable useCleanCache: true
# works ok
mockery.enable useCleanCache: true
Yadda = require 'yadda'
Yadda behaves differently, see also acuminous/yadda#34
There is no difference if modules are actually mocked or not.
In a beforeEach block I am creating and registering a mock:
beforeEach ->
knife = nodemock.mock()
knife.counter = (counter += 1)
console.log("made knife " + knife.counter)
mockery.registerMock('../../knife', knife);
And unregistering it in afterEach:
afterEach ->
mockery.deregisterMock('../../knife');
This mock is loaded by the code under test and executed. When the code under test executes a function on the mock, it prints the value of knife.counter so that I can see which instance it is invoking.
The problem I see is that even though beforeEach and afterEach is registering a new mock for each test, the code under test is invoking on the same instance (instance 1). Therefore the mock receives unexpected additional calls compared to the expectation and the test fails.
Please release a version of this tool, so I can tag version in package.json.
unit tests would be great.
As a sidenote it would be nice to have an examples folder or some pointers to projects that use mockery.
I would like to be a method like: resetCache(), but it will unload only the specified file.
When you call resetCache, you have a big performance issue...
Hi,
I've been having issues on my tests using mockery when I try to register mocks. When I register the mocks with the useCleanCache
flag to false my mocks are not being used because some other test is using the module I'm trying to mock, this is expected behaviour as far as I know. The problem is, I'm using useCleanCache
set to true to avoid this, and that makes my mocked test pass properly, but because some later test is trying to use the same mocked module, my tests fail because the later test gets the mock. I'm calling mockery.disable
and mocker.deregisterAll
after the test where I'm using the mock is finished, but apparently the mock is stored on the node module cache so whenever is required again, I get the mock. Is there a way of forcing the cache to be cleaned after mockery is disabled? I thought that was the default behaviour, but it doesn't seem to be working for me?
Thanks!
why i get this warning every time ?
tests.js
mockery = require "mockery"
mockery.enable
warnOnUnregistered : false
warnOnReplace : true
useCleanCache : true
Later on I mock controllers.js
and this works fine, with the test outputting MOCK CONTROLLER
as expected.
describe '#views', ->
describe '#custom', ->
controller =
custom : (body, success_callback, error_callback) ->
console.log 'MOCK CONTROLLER'
if body
success_callback 'result'
else
error_callback 'error'
mockery.registerMock '../../modules/fixture/controllers.js', controller
views = require '../../modules/fixture/views.js'
custom = views.custom null
it 'should return json keyed by result on success callback', (done) ->
req =
body : true
res.json = (response) ->
response.result.should.eql 'result'
done()
custom req, res
Then when I come to testing a different component, I want to run mockery.resetCache()
before mocking other modules:
describe '#controller', ->
describe '#custom', ->
body =
players : []
# Mock the launcher.
launcher =
launch : (players, map, player_spectate, success_callback, error_callback) ->
if map? && map == 1
success_callback 'success'
else
error_callback [
code : 1
]
# THIS DOESN'T WORK:
mockery.resetCache()
# THIS WORKS:
mockery.deregisterMock '../../modules/fixture/controllers.js'
mockery.registerMock '../../modules/launcher/controllers.js', launcher
custom = require '../../modules/fixture/controllers.js'
.custom
it 'should call the error callback if no players selected', (done) ->
custom body
, () ->
return
, (err) ->
err.should.containEql '6 players'
done()
If I rely on mockery.resetCache()
I still end up with output of MOCK CONTROLLER
. If I use mockery.deregisterMock
however I get the desired effect.
Add in a new type of mock called autoMock
that will require the original module, then smartly stub out all the methods on the module. Putting in place some defaults like:
EventEmmitter
then add some methodsI get the following error, when mockery is enabled:
mockery.enable({
warnOnReplace: false,
warnOnUnregistered: false,
useCleanCache: true
});
[Error: Module did not self-register.]
{ [Error: Cannot find module './build/default/DTraceProviderBindings'] code: 'MODULE_NOT_FOUND' }
{ [Error: Cannot find module './build/Debug/DTraceProviderBindings'] code: 'MODULE_NOT_FOUND' }
My test cases work fine. How do I get rid of these errors?
I have this code from:
https://sazzer.github.io/blog/2015/08/20/Unit-Testing-ES6-Modules-Mockery/
I run the test by BABEL_ENV=ES2015 mocha --require babel-register --recursive tests
But i don't get the mocked name in return, instead i get the original name:
AssertionError: 'Hello, Fred' == 'Hello, Graham'
I am using Node: 4.8.4
"mockery": "^2.1.0"
"mocha": "^4.0.1"
"babel-core": "^6.26.0",
"babel-plugin-module-resolver": "^2.7.1",
"babel-plugin-rewire": "^1.1.0",
"babel-plugin-rewire-exports": "^0.2.2",
"babel-preset-env": "^1.6.1",
"babel-register": "^6.26.0",
"babel-template": "^6.26.0",
"babel-types": "^6.26.0",
Normally this should work, when i use boogie666s atom mocha test runner it's working. But strangely when using mocha / npm the mock is not recognized... anyone knows why?
I want to mock Amazon AWS S3 getObject
The code I want to test is the following one: Its in helper.js
var AWS = require('aws-sdk');
var s3 = new AWS.S3();
exports.get_data_and_callback = function(callback, extra){
s3.getObject( {Bucket: SRC_BUCKET, Key: SRC_KEY},
function (err, data) {
if (err != null) {
console.log("Couldn't retrieve object: " + err);
}else{
console.log("Loaded " + data.ContentLength + " bytes");
callback(data, extra);
}
});
}
In test/helper_test.js
I wrote a test that should mock the module AWS
var assert = require('assert');
var mockery = require('mockery');
describe("helper", function() {
it('loads and returns data from S3 to a callback', function(){
mockery.enable();
var fakeaws = {
S3: function(){
return {
getObject: function(params, callback){
callback(null, "hello")
}
}
}
}
mockery.registerSubstitute('aws-sdk', fakeaws);
function replace_function(err, data){
console.log(data);
}
require('../helper.js').get_data_and_callback(replace_function, null);
});
});
When I require AWS in the Test-File test/helper_test.js
like this:
aws = require('aws-sdk');
s3 = new aws.S3;
s3.getObject(replace_function)
Then my code works, it prints out hello.
BUT the execution of require('../helper.js').get_data_and_callback(replace_function, null);
Doesn't work like expected, AWS stays the same its not replaced with my fakeaws. What do I wrong? Do you maybe have other solutions to replace S3? Thanks
File: lib/movie.js
var Movie = module.exports = {
text: 'newmoon'
}
File: tests/movie.js
describe('movie test', function(){
it('should have mock text', function(){
var movieMock = {
text: 'twilight'
};
mockery.registerMock('movie', movieMock);
var movie = require('movie');
movie.text.should.equal('twilight');
});
});
Command:
$ NODE_PATH=lib mocha -R spec tests
Output:
movie test
1) should have mock text
0 passing (10 ms)
1 failing
1) movie test should have mock text:
actual(red) expected(green)
twilight(green) newmoon(red)
What have I done wrong?
I have a large suite of independent tests that all get run in a single process by mocha. It turns out that different mockery mocks are used in different test cases and this produces a situation where the order that the tests run causes problems since the first mock will still be loaded and cached when the second test runs that uses the same module. (I'm sure that was as clear as mud).
It seems to me that a possible solution to this would be to have the behavior of mockery.registerAllowable(module, true) get run on every module that had a registerMock called on it when deregisterAll gets called. Would that work?
Ideally, we would be able to also have require.resolve
return something different than the actual implementation.
I.e. I'm trying to check for the absence of a module. And in my code, after mocking via mockery.registerMock('koa-bodyparser', null);
the require.resolve('koa-bodyparser')
still returns the actual path, where require('koa-bodyparser')
returns null
.
To allow the best of both ways, this could be an config option to mockery.
Thanks!
What is it that I'm missing?
setup(function() {
mock.enable();
});
teardown(function() {
mock.disable();
});
....
test('should return bad request when provider id is unknown', function(done) {
var db = Helpers.database();
db.getItem = function() {
return function(data, cb) {
console.log('burp');
cb(null, {one: 1});
};
};
mock.registerMock('../database', function() {console.log('mocked');return db;});
request(app).post('/link-players')
.set('authorization', Helpers.fakeAuthorizationHeader())
.send({provider: {id: 'test', metadata: {deviceID: '1', username: 'username'}}})
.expect(400, err(status[400], 'Provider unknown'))
.end(function(err) {
mock.deregisterMock('../database');
done(err);
});
});
'use strict';
var database = require('../database'); // <-----
var xtend = require('xtend');
module.exports = function attachDatabase(options) {
return function middleware(req, res, next) {
options = options == null ? {} : options;
var token = (req.player != null && req.player.accessToken != null) ? req.player.accessToken : null;
if (token != null) {
options = xtend(options, {sessionToken: token});
}
req.db = database(options);
console.log(JSON.stringify(req.db)); // <---- is always the unmocked module
next();
};
};
I'm getting the aforementioned error quite a lot and I'm wondering what general stupidity I'm doing in order to get this error?
I can see a unit-test where this is being exercised, but I don't understand enough about the module-loader internals to understand how I get there.
Any help on the issue is greatly appreciated.
An example:
// src/dependency.js
throw new Error("Shouldn't be required!");
// src/mocked.js
require('./dependency');
// test/mockery_test.js
var mockery = require('mockery');
mockery.enable();
mockery.registerMock('../src/dependency', {});
var mocked = require('../src/mocked');
Running node test/mockery_test.js
will throw the Error. It would be great if registerMock()
recognized paths as being relative to the test file (where the mock is coming from), rather than relative to the file that will require them. Does that make sense?
Just trying to use some destructing.
If I have.
// add.js
const {a, b} = require('./numbers.js');
const add = () => a + b;
module.exports = {add};
The test fails when I go to mock numbers. However, if I rewrite the module to not have destructuring...
const numbers = require('./numbers.js');
const add = () => numbers.a + numbers.b;
module.exports = {add};
My test will pass. It's kinda annoying actually. Is there anything I can do about this? I thought the mockery was suppose to overwrite the require. Or is mockery simply just passing the object around and with destructuring, new objects are created rather than references?
i have one of the dependencies using fs module and due to my test mocks, they are getting affected and hence i would want to know how do i disallow in the dependencies
Similar to #35, when using something like, say express, and registering it as allowable, I get lots of warnings about what isn't allowed. This seems like clutter
WARNING: loading non-allowed module: ./lib/express
WARNING: loading non-allowed module: events
WARNING: loading non-allowed module: merge-descriptors
WARNING: loading non-allowed module: ./application
WARNING: loading non-allowed module: finalhandler
WARNING: loading non-allowed module: debug
WARNING: loading non-allowed module: tty
WARNING: loading non-allowed module: util
WARNING: loading non-allowed module: ./debug
WARNING: loading non-allowed module: ms
WARNING: loading non-allowed module: escape-html
WARNING: loading non-allowed module: on-finished
WARNING: loading non-allowed module: ee-first
WARNING: loading non-allowed module: statuses
WARNING: loading non-allowed module: ./codes.json
WARNING: loading non-allowed module: unpipe
WARNING: loading non-allowed module: ./router
WARNING: loading non-allowed module: ./route
WARNING: loading non-allowed module: debug
WARNING: loading non-allowed module: array-flatten
WARNING: loading non-allowed module: ./layer
WARNING: loading non-allowed module: path-to-regexp
WARNING: loading non-allowed module: debug
WARNING: loading non-allowed module: methods
WARNING: loading non-allowed module: http
WARNING: loading non-allowed module: ./layer
WARNING: loading non-allowed module: methods
WARNING: loading non-allowed module: utils-merge
WARNING: loading non-allowed module: debug
WARNING: loading non-allowed module: depd
WARNING: loading non-allowed module: ./lib/compat
WARNING: loading non-allowed module: buffer
WARNING: loading non-allowed module: events
WARNING: loading non-allowed module: ./lib/compat
WARNING: loading non-allowed module: path
WARNING: loading non-allowed module: array-flatten
WARNING: loading non-allowed module: parseurl
WARNING: loading non-allowed module: url
WARNING: loading non-allowed module: methods
WARNING: loading non-allowed module: ./middleware/init
WARNING: loading non-allowed module: ./middleware/query
WARNING: loading non-allowed module: parseurl
WARNING: loading non-allowed module: qs
WARNING: loading non-allowed module: ./stringify
WARNING: loading non-allowed module: ./utils
WARNING: loading non-allowed module: ./parse
WARNING: loading non-allowed module: ./utils
WARNING: loading non-allowed module: debug
WARNING: loading non-allowed module: ./view
WARNING: loading non-allowed module: debug
WARNING: loading non-allowed module: path
WARNING: loading non-allowed module: fs
WARNING: loading non-allowed module: ./utils
WARNING: loading non-allowed module: content-disposition
WARNING: loading non-allowed module: path
WARNING: loading non-allowed module: content-type
WARNING: loading non-allowed module: depd
WARNING: loading non-allowed module: array-flatten
WARNING: loading non-allowed module: send
WARNING: loading non-allowed module: http-errors
WARNING: loading non-allowed module: setprototypeof
WARNING: loading non-allowed module: statuses
WARNING: loading non-allowed module: ./codes.json
WARNING: loading non-allowed module: inherits
WARNING: loading non-allowed module: util
WARNING: loading non-allowed module: debug
WARNING: loading non-allowed module: depd
WARNING: loading non-allowed module: destroy
WARNING: loading non-allowed module: fs
WARNING: loading non-allowed module: stream
WARNING: loading non-allowed module: encodeurl
WARNING: loading non-allowed module: escape-html
WARNING: loading non-allowed module: etag
WARNING: loading non-allowed module: crypto
WARNING: loading non-allowed module: fs
WARNING: loading non-allowed module: events
WARNING: loading non-allowed module: fresh
WARNING: loading non-allowed module: fs
WARNING: loading non-allowed module: mime
WARNING: loading non-allowed module: path
WARNING: loading non-allowed module: fs
WARNING: loading non-allowed module: ./types.json
WARNING: loading non-allowed module: ms
WARNING: loading non-allowed module: on-finished
WARNING: loading non-allowed module: range-parser
WARNING: loading non-allowed module: path
WARNING: loading non-allowed module: statuses
WARNING: loading non-allowed module: stream
WARNING: loading non-allowed module: util
WARNING: loading non-allowed module: path
WARNING: loading non-allowed module: etag
WARNING: loading non-allowed module: proxy-addr
WARNING: loading non-allowed module: forwarded
WARNING: loading non-allowed module: ipaddr.js
WARNING: loading non-allowed module: qs
WARNING: loading non-allowed module: querystring
WARNING: loading non-allowed module: http
WARNING: loading non-allowed module: ./utils
WARNING: loading non-allowed module: ./utils
WARNING: loading non-allowed module: ./utils
WARNING: loading non-allowed module: depd
WARNING: loading non-allowed module: array-flatten
WARNING: loading non-allowed module: utils-merge
WARNING: loading non-allowed module: path
WARNING: loading non-allowed module: ./router/route
WARNING: loading non-allowed module: ./router
WARNING: loading non-allowed module: ./request
WARNING: loading non-allowed module: accepts
WARNING: loading non-allowed module: negotiator
WARNING: loading non-allowed module: mime-types
WARNING: loading non-allowed module: mime-db
WARNING: loading non-allowed module: ./db.json
WARNING: loading non-allowed module: path
WARNING: loading non-allowed module: depd
WARNING: loading non-allowed module: net
WARNING: loading non-allowed module: type-is
WARNING: loading non-allowed module: media-typer
WARNING: loading non-allowed module: mime-types
WARNING: loading non-allowed module: mime-db
WARNING: loading non-allowed module: ./db.json
WARNING: loading non-allowed module: path
WARNING: loading non-allowed module: http
WARNING: loading non-allowed module: fresh
WARNING: loading non-allowed module: range-parser
WARNING: loading non-allowed module: parseurl
WARNING: loading non-allowed module: proxy-addr
WARNING: loading non-allowed module: ./response
WARNING: loading non-allowed module: content-disposition
WARNING: loading non-allowed module: depd
WARNING: loading non-allowed module: encodeurl
WARNING: loading non-allowed module: escape-html
WARNING: loading non-allowed module: http
WARNING: loading non-allowed module: ./utils
WARNING: loading non-allowed module: on-finished
WARNING: loading non-allowed module: path
WARNING: loading non-allowed module: utils-merge
WARNING: loading non-allowed module: cookie-signature
WARNING: loading non-allowed module: crypto
WARNING: loading non-allowed module: ./utils
WARNING: loading non-allowed module: ./utils
WARNING: loading non-allowed module: ./utils
WARNING: loading non-allowed module: cookie
WARNING: loading non-allowed module: send
WARNING: loading non-allowed module: vary
WARNING: loading non-allowed module: ./middleware/query
WARNING: loading non-allowed module: serve-static
WARNING: loading non-allowed module: encodeurl
WARNING: loading non-allowed module: escape-html
WARNING: loading non-allowed module: parseurl
WARNING: loading non-allowed module: path
WARNING: loading non-allowed module: send
WARNING: loading non-allowed module: url
My Module:
// ./App/Services/MyBridge
import { NativeModules } from 'react-native'
export const MyBridge = NativeModules.MyBridge
export default MyBridge
My Mock:
// ./testSetup.js
mockery.enable()
mockery.registerAllowable('./App/Services/MyBridge', {
nativeCall: () => {}
})
mockery.registerMock('./App/Services/MyBridge', {
nativeCall: () => {}
})
Expected Behavior
MyBridge.nativeCall is mocked
Actual Behavior
Error message during test run Error: Cannot read property 'nativeCall' of undefined
I can't, for the life of me, figure this out. Mocking 3rd party modules works fine.
Any ideas? ([email protected])
Prototype pollution vulnerability in function enable in mockery.js in mfncooper mockery commit 822f056 via the key variable in mockery.js.
The prototype pollution vulnerability can be mitigated with several best practices described here: https://learn.snyk.io/lessons/prototype-pollution/javascript/
In a beforeEach block I am creating and registering a mock:
beforeEach ->
knife = nodemock.mock()
knife.counter = (counter += 1)
console.log("made knife " + knife.counter)
mockery.registerMock('../../knife', knife);
And unregistering it in afterEach:
afterEach ->
mockery.deregisterMock('../../knife');
This mock is loaded by the code under test and executed. When the code under test executes a function on the mock, it prints the value of knife.counter so that I can see which instance it is invoking.
The problem I see is that even though beforeEach and afterEach is registering a new mock for each test, the code under test is invoking on the same instance (instance 1). Therefore the mock receives unexpected additional calls compared to the expectation and the test fails.
I am using mockery to mock some modules. Now I have a special case where I just want to know whether a call to require
with a specific module happened or not. As this has no side-effects that are visible from the outside, I can not test any specific behavior.
Basically I just need to know if require
was called with parameter foo
or not.
How could I test this?
Mockery's restoration of the cache on disable() seems inadequate for addon modules.
I'm using Windows 7, SP2, 64bit, node 0.12.4 (32 bit) and Mockery 1.4.0.
Clone node-addon-examples and add a couple of javascript files to the root folder of the first example: node-addon-examples\1_hello_world\node_0.12
.
File 1: tock.js (12 lines)
var mockery = require('mockery');
// useCleanCache: true is necessary to reproduce the problem
mockery.enable(
{useCleanCache: true}
);
// registerSubstitute() is a workaround for the problem.
// mockery.registerSubstitute('./build/Release/hello.node', './tick.js');
var addon = require('./build/Release/hello.node');
console.log(addon.hello());
mockery.disable();
var addon2 = require('./build/Release/hello.node');
console.log(addon2.hello());
File 2: tick.js (1 line)
module.exports.hello = function () { return "world";};
In a command window, cd to the root folder and run the following commands.
npm install
npm install mockery
node tock.js
The output was the following.
D:\Builds\node-addon-examples\1_hello_world\node_0.12>node tock.js
WARNING: loading non-allowed module: ./build/Release/hello.node
world
module.js:355
Module._extensions[extension](this, filename);
^
Error: Module did not self-register.
at Error (native)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)
at Module.require (module.js:365:17)
at require (module.js:384:17)
at Object.<anonymous> (D:\Builds\node-addon-examples\1_hello_world\node_0.12\tock.js:11:14)
at Module._compile (module.js:460:26)
at Object.Module._extensions..js (module.js:478:10)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)
If we alter the code to turn off useCleanCache
or if we uncomment the line to registerSubstitute()
and rerun, then it runs without error.
I tried a similar test on other examples in this repo of addon examples, with similar results.
A test requires the module I have under test before the test that enables mocking runs. This means my mocks aren't applied, depending on the order that my test suite runs. Rather brittle. How can I get around this?
My fix at the moment is to forcefully delete the required item directly from the require.cache
but that seems ugly. I wish there was a way to always 'hot require' specified paths.
I have no idea what the source of this error is. I'm hoping you can at least point me in the right direction.
import { expect } from 'chai';
import * as mockery from 'mockery';
import * as sinon from 'sinon';
import Spotify from '../src/lib/Spotify';
import ActionTypes from '../src/lib/ActionTypes';
describe('Player', () => {
let player, spotifyStub;
before(() => {
mockery.enable({
warnOnReplace: true,
warnOnUnregistered: true,
useCleanCache: true
});
spotifyStub = sinon.createStubInstance(Spotify);
spotifyStub.emit.restore();
// replace the module `Spotify` with a stub object
mockery.registerMock('./Spotify', spotifyStub);
debugger;
player = require('../src/lib/Player'); // errors
});
after(() => {
mockery.disable();
});
it('should retry connection on spotify error', (done) => {
...
})
});
Errors with:
1) Player "before all" hook:
xxx/src/lib/player.js:1
(function (exports, require, module, __filename, __dirname, process, global) { import Spotify from './Spotify';
^^^^^^
SyntaxError: Unexpected token import
at Object.exports.runInThisContext (vm.js:76:16)
at Module._compile (module.js:513:28)
at Object.Module._extensions..js (module.js:550:10)
at Module.load (module.js:458:32)
at tryModuleLoad (module.js:417:12)
at Module._load (module.js:409:3)
at Function.hookedLoader [as _load] (xxx/node_modules/mockery/mockery.js:111:12)
at Module.require (module.js:468:17)
at require (internal/module.js:20:19)
at Context.before (xxx/app/spec/player.spec.ts:26:14)
at runCallback (timers.js:566:20)
at tryOnImmediate (timers.js:546:5)
at processImmediate [as _immediateCallback] (timers.js:525:5)
Where Player.js starts with an import...
import Spotify from './Spotify';
class Player {
...
I forked mockery to cspotcode/mockery-next. (I know, very creative naming!) I liked mockery's API and needed it for some tests at work, but also wanted support for resolving relative paths. I've added that feature and fixed a few other things. It's on npm.
I'd like to merge back into mockery if possible; no sense having extra forks floating around. However, I've made some breaking changes. Any chance for a 2.0 release and/or can I become a contributor to mockery?
null
substitutions and mockery will pretend the module doesn't exist. For example: mockery.registerSubstitute('some-optional-module', null);
require('mockery')
while useCleanCache
is enabled.I'm having an issue mocking an object that's being instantiated using 'new'. I'm trying to Kinesis from AWS which is being constructed as follows:
var kinesis = new AWS.Kinesis({
apiVersion: kinesisAPIName
});
I'm trying to create a mock object for kinesis:
var mock_aws = {config: {
update: () => {}
},
Kinesis: {
Kinesis: (params) => {},
putRecord: (params, callback) => {
if (params.PartitionKey === 'FAILED_PUT') {
callback('FAILED_PUT');
} else {
callback(null, null);
}
}
}
};
When the Kinesis object is constructed I get a TypeError: AWS.Kinesis is not a constructor
Hello! Thanks for making this nice module! I'm wondering how I can "spy on" a mocked module so that I can make assertions about how it was called.
Suppose I am trying to mock the request-promise
libary which is normally called with request.get
, here's my mockery code:
mockery.registerMock('request-promise', {
get: () => {
return {
statusCode: 200,
body: {
foo: "barracuda"
},
};
},
});
mockery.enable({
warnOnReplace: false,
warnOnUnregistered: false,
});
I would like to assert that request.get
was called with the correct params. I tried setting up a sinon spy as usual, it didn't seem to work. :/
requestSpy = sinon.spy(request, 'get')
Is there any recommended way to do this with mockery?
Thanks! 🙏
So I know the proper way to use registerMock
is to call it before requiring modules in the unit test. However this a little unintuitive for developers unfamiliar with mockery
, and it would make a lot more sense if there was an option to throw an error if the module being mocked has already been required in an earlier statement (say, inside beforeEach
). If this sounds useful, I can submit a PR.
Would it be possible to add a config that will fail the run (i.e. throw an Error) if a module that is not registered either as a mock or as allowed is loaded? The warnOnUnregistered
log output is very easy to miss if you want to be certain that you are not loading something by mistake, especially when the test suite is large.
Something like:
mockery.enable({ throwOnUnregistered: true });
I have been working mockery for some time but since the core API is verbose we have create a wrapper to simplify the use of mockery but i think it would be nicer to have something like that merged in the official repo.
// This is the wrapper we use to mock
function mockRequire(mocks, callback) {
mockery.enable({
warnOnUnregistered: mocks.warnOnUnregistered,
useCleanCache: !mocks.useCache
});
Object.keys(mocks).forEach(function (lib) {
mockery.registerMock(lib, mocks[lib]);
});
try {
callback(mocks);
} finally {
mockery.deregisterAll();
mockery.disable();
}
}
// This are the tests for this feature
describe('#mockReqire', function () {
it('mocks some libs', function () {
var tdMocked;
var fsMocked;
mockRequire({
'../index': 'I am Mocked!',
'fs': 'I am NOT real!'
}, function () {
tdMocked = require('../index');
fsMocked = require('fs');
});
assert.equal(tdMocked, 'I am Mocked!');
assert.equal(fsMocked, 'I am NOT real!');
assert.notDeepEqual(tdMocked, require('../index'));
assert.notDeepEqual(fsMocked, require('fs'));
});
});
// Usecase Example
describe('SomeComponent', function () {
var SomeComponent;
mockRequire({
'../services': mockServices,
'api': mockAPI
}, function () {
SomeComponent = require('../../services/yFinLists/trending_tickers');
});
it('tests stuff on the component', function () {
...
});
The advantage is because you can mock several modules at once and there is not need to use before/after hocks. This feature is very useful for the projects I am working and I would be glad to submit a PR.
Hi,
Thanks for a great tool, we just recently started using this in testing standard-version
.
greenkeeper
notified us of a 2.0
release, but since this project doesn't have a changelog it's pretty hard to see what's changed. Would you consider following the angular
commit convention and using e.g. standard-version
for bumping the version and automatic changelog generation?
Hi, what is the current status of this project? Is it still actively maintained?
How to mock .png of .jpg files,
SyntaxError: D:/BFM Mobile/pushAppcenter/node_modules/react-navigation/src/views/assets/back-icon.png: Unexpected character '�' (1:0)
1 | �PNG
views.js
controllers = require '../../modules/fixture/controllers.js'
exports.custom = (db) ->
(req, res) ->
controllers.custom req.body
, (result) ->
res.json result : result
, (error) ->
res.json 400, error : error
test.js
mockery = require "mockery"
exports.tests = (app, db, config) ->
describe '#routes', ->
describe '/fixture/custom', ->
it 'should return status 400 if no request body was provided', (done) ->
request app
.post '/fixture/custom'
.send
body :
players : []
.expect 400, done
describe '#views', ->
describe '#custom', ->
controller =
custom : (body, success_callback, error_callback) ->
console.log "###MOCK CONTROLLER BODY: #{req}"
if body
success_callback 'result'
else
error_callback 'error'
mockery.enable()
mockery.registerAllowable '../../modules/fixture/controllers.js', true
mockery.registerMock '../../modules/fixture/controllers.js', controller
it 'should return json keyed by result on success callback', (done) ->
req =
body : true
res.json = (response) ->
response.result.should.eql 'result'
done()
custom req, res
I get the error TypeError: Cannot read property 'should' of undefined
which I have confirmed is due to my mock not getting registered as I'd like.
I'm thinking the problem is that require(../../modules/fixture/controllers.js)
is happening before test.tests(app, db, config)
is called, and so the require cache is already created.
Shouldn't mockery.registerAllowable('../../modules/fixture/controllers.js', true)
allow me to override already required modules? I require quite a lot of switching between real and mock modules throughout many tests, so I'd prefer to be able to register mocks "just in time" rather than all at the beginning, for instance before instantiating my express app. Is this possible?
So that no warnings are produced for any non-mocked module. maybe .registerAllowable()
should allow all modules if no module names are passed to it?
When using Mockery to mock out dependencies of modules that are written in ES6 using Babel, there are a number of core Babel modules that get required automatically that need to be marked as allowed to avoid getting warnings.
The example I've found so far is using ES6 classes. When I use Mockery to mock out the dependencies of this module I still get:
WARNING: loading non-allowed module: babel-runtime/helpers/create-class
WARNING: loading non-allowed module: babel-runtime/core-js/object/define-property
WARNING: loading non-allowed module: core-js/library/fn/object/define-property
WARNING: loading non-allowed module: ../../modules/$
WARNING: loading non-allowed module: babel-runtime/helpers/class-call-check
WARNING: loading non-allowed module: babel-runtime/helpers/interop-require-default
I suspect there will be other core imports as well should other parts of ES6/Babel be used.
Hi,
I'm quite new to JS unit testing (I'm more of a PHP backend developper).
I recently worked on an Express API, fully written in ES6 (using babel), and wanted to fortify it with some unit tests.
I managed to setup Mocha & Chai to work like a charm on my "standard" tests, but when it comes to mocks, I can't seem to figure it out. I've tried quite everything I had in mind, but still can't get it to work.
I first needed to mock a class within the elastic search
module, and I couldn't get it to work. So I tried it in some lightweight/sandbox files.
I have lib.js
(the module I want to mock)
export default {
val: () => true
};
Then script.js
(the module I want to test)
import lib from './lib';
export default {
exec: () => {
const val = lib.val();
if (val) {
console.warn('Not mocked');
} else {
console.log('Mocked !');
}
return val;
}
}
And the full test file (script.spec.js
, in the exact same folder as the two others)
import chai from 'chai';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
let should = chai.should();
chai.use(sinonChai);
import mockery from 'mockery';
let libMock = {
val: sinon.stub()
};
libMock.val.returns(false);
mockery.registerSubstitute('./lib', libMock);
mockery.enable({
warnOnUnregistered: false
});
import script from './script'
describe('script', () => {
describe('can mock a lib that', () => {
it('should return false instead of true', () => {
script.exec().should.equal(false);
libMock.val.should.have.been.calledOnce();
});
})
});
And its output (run in IntelliJ Idea) :
Not mocked
AssertionError: expected true to equal false
Expected :false
Actual :true
<Click to see difference>
at Context.<anonymous> (script.spec.js:25:34)
Process finished with exit code 1
Dependencies :
test/mocha.opts
:
--require babel-core/register
--require mocha
--require chai-as-promised
We use mockery also to mock requires on css, svg
and all other files which we include using webpack loaders.
It's more faster than precompile tests with webpack (and also there are a lot of issues using webpack with jsdom together).
So we write something like this
mockery.registerMock('./MayBeInput.scss', {});
mockery.registerMock('./button.scss', {});
...etc
but we want this
mockery.registerMock(/\.scss$/, {});
if I create pull request allowing using regexp, do you accept it?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.