How to spyOn proxied objects with jest? - javascript

There is a scenario where a library exports a proxied object as a public API. Given that the API of the library can't be changed, I failed to find a way how to spyOn a proxied object using jest. Here is an example:
describe("spying on proxied objects with Jest", () => {
it("should pass", () => {
const foo = {
a() {
return 42;
}
};
const p = new Proxy(foo, {
get() {
return () => {
return 53;
};
}
});
const mock = jest.spyOn(foo, "a");
p.a(); // 53
expect(mock).toHaveBeenCalled();
});
});
the test case above fails with:
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
Any ideas?

That's because foo.a is actually never called as your proxy doesn't call your target.
This test passes
describe("Foo", () => {
it("should pass", () => {
const foo = {
a() {
return 42;
},
};
const p = new Proxy(foo, {
get(target, prop) {
return () => {
target[prop](); /* <-- Calling target */
return 52;
};
},
});
const mock = jest.spyOn(foo, "a");
const result = p.a(); // 52
expect(mock).toHaveBeenCalled();
});
});

Related

Using Jest with NodeJS, Function in imported module not being mocked without using 'this' in main

In the setup below, if I run the test as is, myFunc is not mocked when I debug into handler.
However, if instead I add this. in front of the myFunc call in handler, then the function is mocked and everything works as expected.
Can someone please explain why this is? I'm new to mocking and can't see it.
I know what this does, but why won't jest mock without it since I told it to mock that function in the module?
index.js
const aws = require('aws-sdk')
exports.handler = async function (event, context) {
let s;
switch (event.func) {
case "myFunc":
console.log('Executing myFunc');
//making the call: s = await this.myFunc.apply(null, [event.params]) will make the mock work.
s = await myFunc.apply(null, [event.params])
console.log(s);
return s;
/*cases...*/
default:
// default behaviour
}
async myFunc({p1, p2}){
/* do something */
return x
}
exports.myFunc = myFunc
}
index.spec.js
jest.mock('./index.js', () => {
const allAutoMocked = jest.createMockFromModule('./index.js')
const actual = jest.requireActual('./index.js')
return {
__esModules: true,
...allAutoMocked,
myFunc : jest.fn().mockImplementation(() => ({ mockedValue: 'test' })),
handler: actual.handler
}
})
let index = require("./index.js")
describe('Test myFunc', () => {
test('If myFunc function was called', async () => {
var event = { func: 'myFunc', params: { p1: xx, p2: false } };
const context = {};
const logMock = jest.fn((...args) => console.log(...args));
const data = await handler(event, context);
})
})

mocking private methods to test an exported method fails in jest

I'm having an actual class like this
my-file.js
const methodA = () => { return 'output-from-methodA'; }
const methodB = () => { const b = methodA(); b.c = "out-put bind"; return b; }
module.exports = {
methodB
}
my-file.test.js
const { methodA, methodB } = require('./my-file.js');
describe('methodB testing', () => {
it('should call methodA', () => {
methodB();
expect(methodA).toHaveBeenCalled()
}
});
here methodA is private method, so it is not explicit to the test file, then how i ensure it is called or not in the test files
There is no way to test the private function, only the alternative way found is to test the outputs like
const { methodA, methodB } = require('./my-file.js');
describe('methodB testing', () => {
it('should call methodA', () => {
const result = methodB();
expect(result.b.c).toBe("out-put bind")
}
});

Is there a way to use Javascript ES6 Proxy to spy on Object Methods [duplicate]

This question already has answers here:
Why is my function proxy not being called in Node?
(2 answers)
Closed 2 years ago.
Is it possible, given the following object
let target = {
foo:0,
result:[],
bar(){
//some code
}
}
to then wrap said object in a Proxy()
let handler = {
get(){
// code here
},
apply(){
// code here
}
}
target = new Proxy(target,handler);
that catch the call to bar() and save the result to results:[] ?
I've given it a few tries
let target = {
called:0,
results:[],
foo(bar){
return bar;
},
}
let handler = {
get(target,prop){
console.log('Get Ran')
return target[prop];
},
apply(target,thisArg,args){
console.log('Apply Ran')
// never runs
}
}
target = new Proxy(target,handler);
target.foo();
This code misses [[ apply ]] but catches the [[ get ]]
(if memory serves, object method calls are done as two operations , [[ get ]] [[ apply ]])
let target = {
called:0,
results:[],
foo(bar){
return bar;
},
}
let handler = {
get(target,prop){
return target[prop];
},
apply(target,thisArg,args){
let product = target.apply(thisArg,args)
return product;
},
}
let prox = new Proxy(target.foo,handler);
console.log(prox('hello'));
If I instead wrap the object method in a proxy it catches the [[ apply ]]
but I lose the reference to the original object ( this ) and therefore lose access to the result array
I've also tried nesting the method proxy inside of the object proxy.
any thoughts ?
CodeSandBox of my actual code
// Documentation for Proxy
Proxy MDN
Javascript.info/proxy
// Other questions , about proxy , but not this use case
Understanding ES6 javascript proxies
According to spec
A Proxy exotic object only has a [[Call]] internal method if the initial value of its [[ProxyTarget]] internal slot is an object that has a [[Call]] internal method.
So below does catch the invocation if target is a function
(new Proxy(() => 'hello', { apply: () => console.log('catched') }))() // 'catched
Reciprocally, if target does not have a call method, then no proxying
try {
(new Proxy({}, { apply: () => console.log('catched') }))() // throws
} catch (e){ console.log('e', e.message)}
So hint may be to proxy [get] then instead of returning the value (being the function bar, proxy that value to trap the eventual invocation
const p = new Proxy(
{ results: [], bar: () => { console.log('rywhite') } }, {
get: (target, prop) => {
if (prop !== 'bar') return target[prop]
return new Proxy (target.bar, {
apply () {
target.results.push('what')
}
})
}
})
p.bar // nothing bud'
p.bar(); console.log('res', p.results)
p.bar(); console.log('res', p.results)
p.bar(); console.log('res', p.results)
edit: NB: it is not necessary to create a new proxy every time
In code below, returning the same proxy is about twice faster
const N = 1e6
{
const target = { results: 0, bar: () => { console.log('rywhite') } }
const p = new Proxy(
target, {
get: (() => {
const barProxy = new Proxy (target.bar, {
apply () {
target.results++
}
})
return (target, prop) => {
if (prop !== 'bar') return target[prop]
return barProxy
}
})()
})
console.time('go')
for (let i = 0; i < N; ++i) { p.bar() }
console.timeEnd('go')
console.log('res', p.results)
}
{
const p = new Proxy(
{ results: 0, bar: () => { console.log('rywhite') } }, {
get: (target, prop) => {
if (prop !== 'bar') return target[prop]
return new Proxy (target.bar, {
apply () {
target.results++
}
})
}
})
console.time('neweverytime')
for (let i = 0; i < N; ++i) { p.bar() }
console.timeEnd('neweverytime')
console.log('res', p.results)
}

Best way to write a function that either returns a value, or a resolved promise

So I am trying to write a logging HOC that will take a function and log the result of that function. I would like this hoc to be able to log the result of any function whether that function returns a promise or a value. This is what I have so far:
const logFn = (x) => {
return (...args) => {
const result = x(...args);
console.log(`result: ${result}`);
return result;
}
};
I would like to have this function handle the case when x returns a promise. I know a hacky way to do it (typeof result === object && typeof result.then === function) but this seems brittle. I am nearly sure there is a more elegant way to do this but I am struggling to find it.
I have included a failing jest test below:
import logFn from './logFn';
describe('logFn', () => {
let outputData;
beforeEach(() => {
const storeLog = inputs => (outputData += inputs);
console["log"] = jest.fn(storeLog);
require('./logFn');
outputData = ""
});
it('handles async functions', () => {
const add2P = (x, y) => Promise.resolve(x + y);
const logAdd2 = logFn(add2P);
const expected = add2P(1,2).then((data) => data);
const actual = logAdd2(1,2);
expect(outputData).toBe('result: 3');
expect(actual).toEqual(expected);
})
});
bonus points if you can help me clean up the beforeEach.
This has the unfortunate side effect of not logging synchronously, but you could try this.
const logFn = (x) => {
return (...args) => {
const result = x(...args);
Promise.resolve(result).then(function(value) {
console.log(`result: ${value}`);
})
return result;
}
};
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve
While this answer isn't much different from #lemieuxster's answer, there is one major difference.
const logFn = fn => function () {
const result = fn.apply(this, arguments);
Promise.resolve(result).then(value => { console.log(`result: ${value}`); });
return result;
};
This preserves the calling context if, for example, you want to logify member methods:
const logFn = fn => function () {
const result = fn.apply(this, arguments);
Promise.resolve(result).then(value => { console.log(`result: ${value}`); });
return result;
};
const foo = {
bar: 'Hello, world!',
doSomething: logFn(function () {
return this.bar;
})
};
foo.doSomething();

Mocking chained methods

(I use Jest for testing)
For example, I have this function:
const find = () => {
return {
where: () => {
in: () => {}
}
};
};
and I call that in different place:
find('me').where('id').in(['123']);
How to mock and test calls in find(), where() and in()?
Here's a dirt simple mock interface:
const find = (findData) => {
const data = {
find: findData
};
const self = {
where: (whereData) => {
data.where = whereData;
return self;
},
in: (inData) => {
data.in = inData;
return self;
},
data
};
return self;
};
const res = find('me').where('id').in(['123']);
console.log(res.data);

Categories