Jest asses called functions - jestjs

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: ''})
})

Related

JEST Matcher error: received value must be a mock function

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.

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);
});

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")

How to mock API client created inside a test class?

I have a component class as below which create the rest and websocket connections using a third party npm module. I could change the Component.constructor to accept the module as a dependency so that I can inject a mock version during my Jest testing. But I read about Mocks with Jest, I thought I want to try it, but I cannot seem to understand how to intercept Api.Rest() and Api.Websocket return values.
// component.ts
import * as Api from 'npm-module'
import * as wait from 'wait-for-stuff' // actual npm module
export class Component {
private _rest:any;
private _websocket:any;
public events = new EventEmitter();
constructor() {
// I want to intecept the return value of
// Api.Rest() and Api.Websocket() to use mock versions.
this._rest = new Api.Rest();
this._websocket = new Api.Websocket();
this._init();
}
private _init() {
// so that when do stuff with this._rest and this._websocket;
// I can control what is the expected results during test
this._websocket.onUpdate((data) => {
events.emit('update', data);
});
var value = wait.for.promise(this._rest.getSomething());
}
}
Do I have to use another test library like Sinon or Jasmine?
Here is a simplified working example to get you started:
// #ts-ignore
import * as Api from 'npm-module'; // <= (ts-ignore since "npm-module" doesn't exist)
import EventEmitter from 'events';
jest.mock('npm-module', () => {
const getSomethingMock = jest.fn(); // <= always return...
const onUpdateMock = jest.fn(); // <= ...the same mocks...
return {
Rest: () => ({ getSomething: getSomethingMock }),
Websocket: () => ({ onUpdate: onUpdateMock })
}
},
{ virtual: true }); // <= (use virtual since "npm-module" doesn't exist)
class Component {
private _rest: any;
private _websocket: any;
public events = new EventEmitter();
constructor() {
this._rest = new Api.Rest();
this._websocket = new Api.Websocket();
this._init();
}
private _init() {
this._websocket.onUpdate((data) => { // <= ...so that this onUpdate...
this.events.emit('update', data);
});
}
}
test('Component', () => {
const component = new Component();
const listener = jest.fn();
component.events.on('update', listener);
const onUpdate = new Api.Websocket().onUpdate; // <= ...is the same as this one
const onUpdateArrowFunction = onUpdate.mock.calls[0][0]; // <= get the arrow function passed to it
onUpdateArrowFunction('mock data'); // <= now call the function
expect(listener).toHaveBeenCalledWith('mock data'); // Success!
});
Details
Jest takes over the require system and allows you to specify what you want it to return when a module is required (note that TypeScript import statements get compiled to require calls).
One way to mock a module is to create a manual mock by creating a file at __mocks__/npm-module.ts that contains your mock.
Another way (show above) is to use jest.mock and pass it a module factory function.
Whenever the module is required during the test Jest will return the mocked module instead.
Note that the example above always returns the same mock for getSomething and onUpdate so those mock functions can be retrieved during the test.
Also note the use of mockFn.mock.calls to retrieve this arrow function:
(data) => {
this.events.emit('update', data);
}
...which gets passed to onUpdate. Once it has been retrieved, it can be called directly which triggers the listener as expected.

How to run async functions in before hook in MochaJS?

I try to call a lot of async functions in my mocha JS before hook but they are executed at last. Basically I am trying to execute repeated tests with different params from the constructor initialization.
I tried with only one function but it also execute at last.Tried to pass done() function to inner async function but it doesnt help either.
a.test.js and base.tests.js files :
describe('Test block', () => {
before((done) => {
const baseClass = new baseClass()
baseTests.applyTests(done)
});
describe('test',()=>{
....first which should be executed;
})
}
----------------------------------------------------------------
class baseClass {
constructor() {
init smth....
}
async applyTests(done) {
await Promise.All(
[
a(),
b(),
c()
]
done();
)
}
async a() {
return describe('TEST', () => {
it('TEST', (done) => {
chai.request(server)
.get(url)
.end((err, res) => {
asserts...
done();
});
});
}}
I expect to run first the async operations in the before hook and after that all other tests.
Keep in mind that describe and it blocks do not execute any tests; they add tests to the test list that Mocha will execute. Putting a describe block inside a function called from a before hook like this won't ever end up executing code like chai.request(... etc. (And even if it did, it would be totally broken: a before hook is run before each test, you don't want to do asserts there because it won't be linked to any particular unit test.)
I can't give more concrete advice because I'm not sure what you were trying to accomplish, but in general, your describe and it blocks should be top-level constructs and not put inside other code like this.
EDIT: Just to make the execution path here clear: your before hook runs, which calls applyTests, which calls a(), which executes a describe block and adds your unit test TEST to the test list, then it returns. It then begins running tests, including test and TEST.
EDIT: Ah, makes sense, let me suggest two patterns that are often used in unit tests.
Pattern 1: "Test Loop"
This pattern creates many similar-looking tests, using an array of input params to produce appropriate test descriptions and test bodies.
[
{ foo: "milk", bar: "smoothie" },
{ foo: "yogurt", bar: "fridge" },
{ foo: "whatever", bar: "container" }
].forEach(test => {
it("puts the " + test.foo + " in the " + test.bar, function () {
assert(subject.someMethod(foo) === bar);
});
});
Pattern 2: "Test Helper"
This pattern creates individual tests, but puts a common test body in a helper method so it can be reused.
function testSomeMethod(foo, bar) {
assert(subject(foo) == "bar");
}
it("puts the milk in the fridge", function () {
testSomeMethod("milk", "fridge");
});
it("puts the cereal in the pantry", function () {
testSomeMethod("cereal", "pantry");
});
Examples above are very simple, but either the test loop or the test helper pattern can be used to encapsulate a much more complicated series of steps (set up a request, look at some response headers / bodies, etc.).

Resources