Internal vs Exported References to Module Methods - javascript

I'm trying to better understand the how & why of differences between references to an internal module method vs the reference that is created when that same method is exported.
I'm not even sure my terminology is correct, and searching for information has not turned up anything useful.
A simplified example ...I have a simple module that performs some maths:
export const doubleNumber = (num) => {
return num * 2
}
export const squareNumber = (num) => {
return num * num
}
export default {
double: (num) => doubleNumber(num),
square: (num) => squareNumber(num),
}
...and I have some unit tests to verify functionality:
import * as numProvider from './number-provider'
describe('the default methods', () => {
it('should call the correct math method when its property method is called', () => {
const doubleSpy = jest.spyOn(numProvider, 'doubleNumber')
const result = numProvider.default.double(1)
expect(result).toBe(2)
expect(doubleSpy).toHaveBeenCalledTimes(1)
})
})
(example project with the above code)
Now when I run my test it fails. From what I understand, the method I'm spying on (the exported method), is never called because numProvider.default.double is referencing the internal method.
I can verify this (and fix the test) by attaching the exported method to the default export object instead, double: (num) => exports.doubleNumber(num) but then of course the site breaks because exports is not defined in the browser.
So my question (I think?) is..
What is JavaScript (or some other process?) doing that causes the creation of these two separate references?

What is JavaScript (or some other process?) doing that causes the creation of these two separate references?
The crux of the issue is that exports are properties on an object that contain separate references to functions within the module and you're only spying on function calls made through the exported property on the object. You're not spying on the underlying function itself so calls made directly to the underlying function are not monitored.
In fact, the Javascript language doesn't provide a way to spy on the underlying core function when all you have is a reference to it. Calls to the core function are made through whatever reference you have. Declaring a function creates a symbol and assigns it a reference to the function you declared. Declaring an export for that creates a property on an object and assigns it another reference to the same underlying function.
Spying on the object hooks the function reference in that object (replaces it with a monitoring function) and it can only monitor calls that are made through that object property because only those will actually call the replacement monitoring function.
I'll give you an example using plain objects so you can see what's going on without the added distraction of exports:
// simple version of spyOn
const spy = function(obj, methodName) {
let orig = obj[methodName];
obj[methodName] = function() {
console.log(`spyhit for obj.${methodName}()`);
}
}
// core function
const doubleNumber = function(num) {
return num * 2
}
// object that contains reference to core function
const myObj = {
double: doubleNumber
};
// lets spy on myObj.double
spy(myObj, "double");
myObj.double(1); // generates spyhit
doubleNumber(1); // does not generate spyhit
Here you can see that only calls made through the actual property that you spied on myObj.double() are actually spied on. There's no spying of the actual core function.
This is because myObj.double is all that the spy function is given. That property contains a reference to doubleNumber, but isn't the actual function itself. The spy() method here (similar to what jest is doing) just replaces the actual property with a monitoring function so that it can record when it's called. But, it has no ability to replace the actual core function itself.

Related

How to test an internal function

I'm trying to create a unit test to cover an internal function
fileA.js
module.exports.functionA = () {
const functionB = () {
// do something
}
functionB()
}
test.js
const { functionA } = require('fileA')
...
it('runs functionB', () => {
functionA()
expect(...).toHaveBeenCalled()
}
How do I access it?
There are two possibilities here (your situation looks like the first one, but is also clearly simplified for the purposes of the question).
Either:
functionB is entirely private to functionA, part of its implementation, which means you can't access it to test it (directly). Instead, test functionA, which presumably uses functionB as part of the work it does (otherwise, there's no point in an entirely private function in functionA).
or
functionA exposes functionB in some way (for instance, as a return value, or as a method on a returned object, or by setting it on an object that's passed in, etc.), in which case you can get it in whatever way functionA provides and then test it.
(Again, I think yours is the first case.)

How to test that an inner function has been called from an imported function? (with Jest.js)

I'm having issues with Jest testing that a closure (an inner function) has been called after calling the outer function. I've tried using the spyOn with no positive result. This seems to be a relatively simple problem, to which I haven't found any results from googling.
// helper.js
export const bar = () => {}
export const foo = () => {
bar(); //How do I test that this has been called?
}
//helper.test.js
import * as H from 'helper';
const barSpy = jest.spyOn(H, 'bar');
H.foo();
expect(barSpy).toHaveBeenCalled(); // Jest throws error "Expected mock function to have been called."
I had the same issue once, and found this article which is very well explained: https://www.exratione.com/2015/12/es6-use-of-import-property-from-module-is-not-a-great-plan/
From the article:
Stubbing a function in Javascript requires the function to be bound to a context, any context, that is in scope for both the test code and the code being tested. In a sane world this context is provided by the module. For example, in ES5 Node.js:
exports.fn = function () {}
The thing to avoid doing in ES5 is the following, overriding module.exports with a function. All too many people do this and it is inconsiderate, as any module using that code must then take extra steps to be usefully unit tested:
module.exports = function () {}
So the last exported function can't be stubbed since it's not bound to any context, theres a workaraound for that, bounding the function to the exports object of the module you are testing and calling it as exports.importedFunction, like so:
var example = require('./example');
// Exported for test purposes. If we don't do this, then
// example is encapsulated here and cannot be stubbed.
exports.example = example;
// Usage.
exports.invokeExample = function () {
return exports.example();
};
Then you'll be able to spy on it, but you had to write that extra code, which is kinda ugly and not very useful neither clear.
In ES6 using "import { x } from 'y'" is analogous to overwriting module.exports with a function in ES5. The result is that an imported function is encapsulated in the module and cannot be stubbed in unit tests without writing more boilerplate code that would otherwise have been the case.
This also happens for your case: import * as H from 'helper'

How to properly stub a function return value?

Premise: JS ES6, NodeJS
Testing Framework: TAP
Mocking Library: testdouble.js
I am attempting to mock the return value for the method of my class and keep receiving this error:
not ok Unsatisfied verification on test double. Wanted: - called with (true). But there were no invocations of the test double.
Here is my testing code:
// Imports for unit testing
const tap = require('tap');
const Subject = require('../src/iTunesClient.js');
const td = require('testdouble');
let reqJson;
// Ensure the iTunes class methods are called
tap.test('iTunesClient class methods function as intended', (t) => {
t.beforeEach((ready) => {
reqJson = td.replace('../src/reqJson.js');
ready();
});
t.afterEach((ready) => {
td.reset();
ready();
});
t.test('iTunesClient.getData', (assert) => {
const callback = td.function();
const subject = new Subject();
subject.setTerm('abc 123');
subject.setURL();
td.when(reqJson.get(td.callback)).thenCallback(true);
subject.getData(callback);
td.verify(callback(true));
assert.end();
});
t.end();
});
Specifically, this line is related to my issue:
td.verify(callback(true));
How can I fake the callback value of true for reqJson.get()? Right now, Subject.geData() is a method of the iTunesClient class which calls another file, reqJson.js, to use its exported get() method.
It's a little hard to tell from your example, but it looks like you're requiring iTunesClient before you call td.replace. In this case, the real reqJson module will be required and cached on line 3.
You need to call td.replace early enough to avoid this, e.g. in between requiring tap and iTunesClient.
I wanted to update this question, as I recently solved this issue. Essentially, I had two issues:
Account for both reqJson function parameters
Account for all callback return values
Per testdouble documentation for item 1:
When passed td.matchers.anything(), any invocation of that test double function will ignore that parameter when determining whether an invocation satisfies the stubbing.
Hence, I adjusted my line of code as follows:
Before: td.when(reqJson.get(td.callback)).thenCallback(true);
After: td.when(reqJson.get(td.matchers.anything(), td.callback)).thenCallback(null, null, null);

Vue.JS + webpack build issue with event emission

Using the eventBus pattern in Vue.js allows a central place to emit events so that subscribing listening components can handle such events.
The below code snippets are setting up listeners on child components to receive an updated Object server when a particular UI change happens.
I ran into something today where this DID'T work in a child component:
created: function() {
eventBus.$on('serverSelected', function(server) {
console.log('serverDetails, server=' + server.toString());
this.server = server;
});
},
but this DID work:
created: function() {
eventBus.$on('serverSelected', (server) => {
console.log('serverDetails, server=' + server.toString());
this.server = server;
});
},
I believe the only different is the ES6 syntax for the callback. But the vanilla JS should still work right?
I'm very new to JS. Why is there a different and why does only the second version work?
A major difference between function(){} and () => {} is precisely how this will be handled inside the scope of the function.
With the arrow function (() => {}), this will be handled lexically; meaning it will point to the containing scope.
From the MDN documentation linked above,
An arrow function does not create its own this context, so this has
its original meaning from the enclosing context
With the regular function expression, what this refers to depends on how the method is called. In your case it probably refers to eventBus.
Again, from the MDN documentation,
Until arrow functions, every new function defined its own this value
(a new object in the case of a constructor, undefined in strict mode
function calls, the context object if the function is called as an
"object method", etc.).
That's why it works with the arrow function, but not with the regular function, because with the arrow function, this points to the Vue that has the server property, and with the regular function, it points to the eventBus.
Here is an example showing the difference. Pop open the console to see the different messages.
If you wanted to continue to use the regular function, you would need to bind this appropriately. One way is to use bind.
created: function() {
eventBus.$on('serverSelected', function(server) {
console.log('serverDetails, server=' + server.toString());
this.server = server;
}.bind(this));
},
Or you could use a closure.
created: function() {
const self = this
eventBus.$on('serverSelected', function(server) {
console.log('serverDetails, server=' + server.toString());
self.server = server;
});
},
See also, How to access the correct this inside a callback?

Javascript: Mocking Constructor using Sinon

I am pulling my hair out trying to figure out how to mock a constructor using sinon. I have a function that will create multiple widgets by calling a constructor that accepts a few arguments. I want to verify that the constructor is called the correct number of times with the correct parameters, but I don't want to actually construct the widgets. The following links seemingly explain a straightforward way of mocking the constructor, however it does not work for me:
Spying on a constructor using Jasmine
http://tinnedfruit.com/2011/03/25/testing-backbone-apps-with-jasmine-sinon-2.html
When I make the following call to stub the constructor:
sinon.stub(window, "MyWidget");
I get the following error:
Uncaught TypeError: Attempted to wrap undefined property MyWidget as function
When debugging in Chrome I see MyWidget shows up in the Local section of the Scope Variables, however there is not MyWidget property off of window.
Any help would be greatly appreciated.
I needed a solution for this because my code was calling the new operator. I wanted to mock the object that the new call created.
var MockExample = sinon.stub();
MockExample.prototype.test = sinon.stub().returns("42");
var example = new MockExample();
console.log("example: " + example.test()); // outputs 42
Then I used rewire to inject it into the code that I was testing
rewiredModule = rewire('/path/to/module.js');
rewiredModule.__set__("Example", example);
From the official site of sinonjs:
Replaces object.method with a stub function. The original function can be restored bycalling object.method.restore(); (or stub.restore();). An exception is thrown if the property is not >already a function, to help avoid typos when stubbing methods.
this simply states that the function for which you want to create the stub must be member of the object object.
To make things clear; you call
sinon.stub(window, "MyWidget");
The MyWidget function needs to be within the global scope (since you pass window as parameter). However, as you already said, this function is in a local scope (probably defined within an object literal or a namespace).
In javascript everyone can have access to the global scope, but not the other way around.
Check where you declare the MyWidget function and pass container object as first parameter to sinon.stub()
Using Sinon 4.4.2, I was able to mock an instance method like this:
const testObj = { /* any object */ }
sinon.stub(MyClass.prototype, "myMethod").resolves(testObj)
let myVar = await new MyClass(token).myMethod(arg1, arg2)
// myVar === testObj
A similar solution provided here:
Stubbing a class method with Sinon.js
I used Mockery to Mock a Constructor/Function without any problems.
var mockery = require('mockery');
var sinon = require('sinon');
mockery.enable({
useCleanCache: true,
warnOnReplace: false,
warnOnUnregistered: false
});
exports.Client = function() {/* Client constructor Mock */};
var ClientSpy = sinon.spy(exports, 'Client');
mockery.registerMock('Client', ClientSpy);
var Factory = require('Factory'); // this module requires the Client module
You should be able to apply a Sinon Spy just as the example above does.
Make sure to disable or reset Mockery after the test(s)!
Use sinon.createStubInstance(MyES6ClassName), then when MyES6ClassName is called with a new keyword, a stub of MyES6ClassName instance will returned.
I ran into this error by mistakenly typing sinon.stub.throws(expectedErr) rather than sinon.stub().throws(expectedErr). I've made similar mistakes before and not encountered this particular message before, so it threw me.
Mocking and stubbing the constructor with sinon
I give two solutions. The first addresses the question, how to mock the constructor with behaviour, the second shows how to stub it with a dummy. Google guided me multiple times to this question for the search how to stub it.
Mocking the constructor with behaviour
I don't know if this is the shortest path. At least it does, what was asked for.
First I use sinon's fake method to create a mocking constructor Mock with the behaviour I want. Then I have to add the methods one by one. For reasons I didn't investigate, it did not work, when setting the whole prototype of UnderTest to Mock.
require('chai').should();
const { fake} = require('sinon');
class UnderTest {
constructor() {
this.mocked = false;
}
isMocked() {
return this.mocked;
}
}
describe('UnderTest', () => {
let underTest;
let isMocked;
before(() => {
const Mock = fake(function () { this.mocked = true; });
Mock.prototype.isMocked = UnderTest.prototype.isMocked;
underTest = new Mock();
isMocked = underTest.isMocked();
});
it('should be mocked', () => {
isMocked.should.be.true;
});
});
Stubbing the constructor with a dummy
If you are leaded to this post, because you just want to stub the constructor to keep it from being executed.
Sinon's createStubInstance creates a stubbed constructor. It also stubs all methods. Hence, the method under test has to be restored before.
require('chai').should();
const { createStubInstance } = require('sinon');
class UnderTest {
constructor() {
throw new Error('must not be called');
}
testThis() {
this.stubThis();
return true;
}
stubThis() {
throw new Error('must not be called');
}
}
describe('UnderTest', () => {
describe('.testThis()', () => {
describe('when not stubbed', () => {
let underTest;
let result;
before(() => {
underTest = createStubInstance(UnderTest);
underTest.testThis.restore();
result = underTest.testThis();
});
it('should return true', () => {
result.should.be.true;
});
it('should call stubThis()', () => {
underTest.stubThis.calledOnce.should.be.true;
});
});
});
});
Just found this in the documentation.
If you want to create a stub object of MyConstructor, but don’t want the constructor to be invoked, use this utility function.
var stub = sinon.createStubInstance(MyConstructor)
I was able to get StubModule to work after a few tweaks, most notably passing in async:false as part of the config when requiring in the stubbed module.
Kudos to Mr. Davis for putting that together

Categories