JEST Matcher error: received value must be a mock function - node.js

I am receiving "Matcher error: received value must be a mock function" trying to compare the result of the completed function. I do have 1 mock statement that mocks 2 methods from the utilities module for the index module to complete successfully when referencing those. I assume, though, that I do not need to mock index modules in order to pass the test. Here's the whole test that's not working:
import * as index from '../src/index';
jest.mock('../src/utils', () => {
const originalModule = jest.requireActual('../src/utils');
return {
__esModule: true,
...originalModule,
downloadModule: jest.fn(() => 'downloaded'),
compareChecksum: jest.fn(() => true)
}
});
describe('testing ingex file', () => {
test('testing case with mocked functions', async () => {
await expect(index.execute()).resolves.toHaveReturnedWith(undefined);
});
});
Utils is just a file with series of useful methods used in index. While the function called in index looks like this (Redacted):
//src/index
export async function execute() {
switch(type) { // comes from env vars
case 'file': {
await downloadModule(params); // mocked to succeed
if (await compareChecksum(otherParams)) {// mocked to succeed
console.log("Equal");
return;
}
...
}
...
}
}
The complete error I am getting:
expect(received).resolves.toHaveReturnedWith(expected)
Matcher error: received value must be a mock function
Received has value: undefined
So index module is dependent on utils and I mocked all necessary methods for it to pass successfully. Received value just cannot be a mock function ever, and it does receive the "undefined" result as expected but refuses to compare it properly. Not sure why the expected result is supposed to be a mock function in this case.
In case it matters this is typescript (not clearly visible from the code provided) and ts-jest is installed.

Related

importing and calling a function in Typsescript which returns nothing

Hi I have a function in typescript which returns nothing. When I try to import and call this function in another part of my app, I am getting errors. I am fairly new to typescript and am struggling with how to fix this issue.
Here's my code. I am trying to set up a few scripts to do some simple tests (would prefer not to use any testing frameworks).
My helper functions for testing are here
//File: helper.ts
type validator = () => void;
export const it = (desc: string, fn: validator) => {
try {
let res = fn();
console.log("\x1b[32m%s\x1b[0m", `\u2714 ${desc}`);
} catch (error) {
console.log("\n");
console.log("\x1b[31m%s\x1b[0m", `\u2718 ${desc}`);
console.error(error);
}
};
My tests use the helpers and are defined like this;
// File: dummytests.ts
import { strict as assert } from 'node:assert';
import { it } from "src/spec/helper";
export const check_if15_eqls_15 = it("shoulld check if something is true", () => {
assert.strictEqual(15, 15);
});
and i finally am running the tests as such;
// File: testRUnner.ts
import {check_if15_eqls_15} from 'src/spec/frontend/dummyTests';
console.log ("This test harness is for the frontend tests.\n");
check_if15_eqls_15();
this throws the error;
error TS2349: This expression is not callable.
Type 'void' has no call signatures.
5 check_if15_eqls_15();
~~~~~~~~~~~~~~~~~~
The line
export const check_if15_eqls_15 = it("shoulld check if something is true", () => {
assert.strictEqual(15, 15);
});
already calls the method.
This means check_if15_eqls_15 is already the return value of the method and unless the return value is not another function (which it isn't in this case), you can't call it again.
The same thing would happen in pure JS, since TypeScript does not change how the code is run
Something that might work for your example would be this:
export const check_if15_eqls_15 = () => it("shoulld check if something is true", () => {
assert.strictEqual(15, 15);
});

Handle async imports in jest.mock factory

I am reusing db mocks in different tests so I created aka factories for mocking a single table and calling them in jest.mock()
jest.mock('db', () => {
const {
UsersMockFactory,
RequestMockFactory
} = jest.requireActual('../../../../mocks');
return {
Users: UsersMockFactory(),
Requests: RequestMockFactory(),
};
});
The problem is that 19 tests will pass with mock from that file but 20th will throw an error RequestMockFactory is not a function.
I've tried using const mocks = require('../../../../mocks') and then console.log the result but RequestMockFactory still wasn't there for some reasons. I don't know why but it is the main reason I have this problem. I've tried to use await import() like this and it imports correctly:
jest.mock('db', async () => {
const {
UsersMockFactory,
RequestMockFactory,
} = await import('../../../../mocks');
return {
Users: UsersMockFactory(),
Requests: RequestMockFactory(),
};
});
Now the problem is that mock factory returns a Promise for an entire mock and obviously methods on a model are undefined.
I've tried to mock inside describe() , beforeAll(), inside it() - jest.mock is not working there.
How to handle imports in jest.mock without such pain?

Spying on a prototype method in Sinon.js

With sinon, I'm trying to spy on a prototype method. It looks so simple in online howtos etc. I tried many websites and SO posts like this: Stubbing a class method with Sinon.js or sinon - spy on toString method, but it just doesn't work for me.
Prerequisites
I'm using http.d.ts https://github.com/nodejs/node/blob/master/lib/_http_outgoing.js to write data back from an async API call through an OutgoingMessage object:
class OutgoingMessage extends stream.Writable
There is a prototype method end in OutgoingMessage:
OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {
My API function is called like this:
Get = async (req:IncomingMessage,res:OutgoingMessage):Promise<void> => {...
...
res.end(data)
...
}
My tests
In my test I'm calling the Get method. My IncomingMessage determines what I expect to be in the OutgoingMessage.
it("should call end with the correct value", async function(){
...
let outgoingMessageSpy = sinon.spy(OutgoingMessage.prototype,"end");
let anOutgoingMessage = <OutgoingMessage>{};
...
expect(outgoingMessageSpy.calledOnce).to.be.true();
}
Debugging the test case I see how end is being called but apparently I have not set up my spy the right way as my expectation fails, calledOnce is false. Inspecting the object I see that calledCount is 0.
I'm doing basically the same (it appears to me) when I do
const toStringSpy = sinon.spy(Number.prototype, "toString");
expect(toStringSpy.calledWith(2)).to.be.true;
and that works. I do notice, though, that VS Code highlights the keyword prototype differently for Number.prototype and OutgoingMessage.prototype. Is that of relevance? On mouse-over is says NumberConstructor.prototype but only OutgoingMessage.prototype..
Questions
How to set up the spy correctly to pick up the call to the prototype method end?
You have set up the spy correctly. BUT, there are conditions which can make the spy fail. For example: I saw you use async function, maybe you are not awaiting correctly on your async function test. Example below to show you that condition.
I have a very simple http server which has response with delay.
I create your Get method like this:
// File: get.ts
import { IncomingMessage, OutgoingMessage } from 'http';
import delay from 'delay';
const Get = async (req: IncomingMessage, res: OutgoingMessage): Promise<void> => {
console.log('Get start');
// I add this to show you that this async function need to be awaited.
await delay(200);
res.end(JSON.stringify({
data: 'Hello World!'
}));
console.log('Get finish');
};
export default Get;
And I create the main index.ts file.
// File: index.ts
import http, { IncomingMessage, OutgoingMessage } from 'http';
import Get from './get';
const server = http.createServer((req: IncomingMessage, res: OutgoingMessage) => {
console.log('Receiving IncomingMessage');
res.setHeader('Content-Type', 'application/json');
Get(req, res);
});
const port = 8000;
server.listen(port);
server.on('listening', () => {
console.log(`Listen http on ${port}`);
});
I create the test file: get.spec.ts
// File: get.spec.ts
import sinon from 'sinon';
import http, { OutgoingMessage } from 'http';
import { Socket } from 'net';
import { expect } from 'chai';
import Get from './get';
describe('GET', () => {
it('should call end with the correct value', async () => {
// I copy paste from your code with minor code style edit.
const outgoingMessageSpy = sinon.spy(OutgoingMessage.prototype, 'end');
const socket = new Socket();
const incoming = new http.IncomingMessage(socket);
const outgoing = new http.OutgoingMessage();
// This is just some private property which need to be set.
(outgoing as any)._header = true;
// NOTE: If you want invalid response, remove the "await".
await Get(incoming, outgoing);
// Set the debug message here before expect.
console.log('SPY Counter:', outgoingMessageSpy.callCount);
console.log('SPY CalledOnce:', outgoingMessageSpy.calledOnce);
expect(outgoingMessageSpy.calledOnce).to.be.true;
});
});
When I run using ts-mocha from terminal, the result is like this:
$ npx ts-mocha get.spec.ts
GET
Get start
Get finish
SPY Counter: 1
SPY CalledOnce: true
✓ should call end with the correct value (204ms)
1 passing (206ms)
You see that you have setup the spy to OutgoingMessage.prototype.end correctly.
BUT, when you remove await inside the test (see NOTE inside get.spec.ts file), this is the result:
$ npx ts-mocha get.spec.ts
GET
Get start
SPY Counter: 0
SPY CalledOnce: false
1) should call end with the correct value
0 passing (8ms)
1 failing
1) GET
should call end with the correct value:
AssertionError: expected false to be true
+ expected - actual
-false
+true
Get finish
The condition is: end method get called correctly but after the it test has been evaluated.

jest-mock-extended trying to return promise value

I'm using jest-mock-extended and i'm trying to mock out a public function on a class interface thats been mocked.
Here is my IApiService
export default interface IApiService {
send: (url: string) => Promise<any>;
}
My Test for homeService class which has only one public function called start.
import { mock } from "jest-mock-extended";
import HomeService from "./homeService";
import IApiService from "../Api/interface";
describe("HomeService", () => {
let apiService: IApiService;
let service: HomeService;
beforeEach(() => {
apiService = mock<IApiService>();
service = new HomeService(apiService);
});
it("Should shit all over the place", () => {
const mock_fetch = jest.fn(() => Promise.resolve("response"));
apiService.send.mockReturnValue(mock_fetch);
service.start();
expect(mock_fetch).toBeCalled();
});
});
I'm trying to get send method to return a mocked value but none of the functions on mock seem to work the way I'm trying to call them.
Since Jest spy API is used on mocked object, it should be properly typed, as suggested in the documentation:
let apiService: MockProxy<IApiService>;
This merges method types with Jest spy types, send type is something like:
((url: string) => Promise<any>) & jest.SpyInstance<Promise<any>>
Mocked function is expected to return a promise, not a function that returns a promise, so it should be mocked as:
apiService.send.mockReturnValue(Promise.resolve("response"))
or
apiService.send.mockResolvedValue("response")

Jest asses called functions

I am testing my graphql endpoint with Jest. Basically I am assessing the result returned from resolver. But in this case, I want to test if a function has been called inside the resolver with particular parameter. The code is something like
import { sendLib } from '../lib'
export default {
send: async (input) => {
const data = {
foo: 1,
bar: 2,
...input
}
await sendLib(data)
return { input }
}
}
I want to make sure sendLib() is called with data parameter. The most important thing is I need to test if data has correct values. I have been reading the Jest's .toHaveBeenCalledWith() but I can't figured out how it fits my case.
Also is there a way to do that test without being actually executing sendLib()? Because the function calls a HTTP request and I don't really want to waste time to do that on testing.
you should mock lib in your test wit jest.fn that way it would not call your actual function implementation or perform http request. instead it would call the mock function. and you can assert what it was called with below is a rough example of what i'm tall
// filetotest.js
import { sendLib } from '../lib'
export default {
send: async (input) => {
const data = {
foo: 1,
bar: 2,
...input
}
await sendLib(data)
return { input }
}
}
//__tests__/filetotest.test.js
import { sendLib } from '../../lib'
import defaultImport from '../filetotest.js'
jest.mock('../../lib/', () => {
return {
sendLib: jest.fn()
}
})
test('call with correct data', async () => {
// call the function you are suppose to to test
await defaultImport()
// sendLib is a mockFn as jest.mock calls are hoisted
expect(SendLib).toHaveBeenCalledWith({data: ''})
})

Resources