How to mock specified request use jest? - jestjs

I want to mock a specified request use jest. For example: in a method, I want to mock like axios.get(uri_1).mockResolvedValue(resp), but I not want mock uri_2 in the same method. What should I do in this situation?

mockFn.mockResolvedValue(value) is syntactic sugar for:
jest.fn().mockImplementation(() => Promise.resolve(value));
In your case adding a few conditional logic might be enough. You don't even need Jest's mocks.
import axios from 'axios';
const axiosGetOriginal = axios.get;
axios.get = (uri) => {
if (uri === 'uri_1') {
return Promise.resolve(value);
}
// Return original behaviour
return axiosGetOriginal(uri);
};

Related

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?

Nestjs: Retrieve the request / context from a Decorator

I am working on a NestJS project,
I'm trying to get the executionContext accessible in a logger to filter the logs by request.
I have one logger instance per injectable, and I would like to keep this behavior (So the scope of the injectable is default).
To do this, I'm trying to create a decorator that gets the context from the request and passes it to the child services (as in the logger), to finally get the context in the logger...
I'm not sure to be clear... For now, here is my code:
export const Loggable = () => (constructor: Function) => {
for (const propertyName of Reflect.ownKeys(constructor.prototype)) {
let descriptor = Reflect.getOwnPropertyDescriptor(constructor.prototype, propertyName);
const isMethod = descriptor.value instanceof Function;
if (!isMethod)
continue;
const originalMethod = descriptor.value;
const routeArgsMetada = Reflect.getMetadata(ROUTE_ARGS_METADATA, constructor, propertyName as string);
descriptor.value = function (...args: any[]) {
const result = originalMethod.apply(this, args);
//TODO : retrieve the request / contextExecution
//TODO : pass the request / contextExecution to children functions...
return result;
};
Reflect.defineProperty(constructor.prototype, propertyName, descriptor);
Reflect.defineMetadata(ROUTE_ARGS_METADATA, routeArgsMetada, constructor, propertyName as string);
}
};
This #Loggable() decorator would be attached to all injectable classes that need to log or throw execution context
Is that possible ? If not why ?
PS: I'm wondering, how could the #Guard annotation get the context? and how could the #Req annotations get the request?
https://github.com/nestjs/nest/tree/master/packages/common/decorators/http
https://github.com/nestjs/nest/blob/master/packages/common/decorators/core/use-guards.decorator.ts
How #Req does get the Request?
Download source of NestJS from here: https://github.com/nestjs/nest
and look for 'RouteParamtypes.REQUEST' in TS files. You will find them here:
route-params.decorator.ts
route-params-factory.ts
As you can see decorators generally don't do too much. They just add some metadata to classes, methods, and arguments. All the rest do the framework.
Here #Req only creates a special parameter decorator during startup which is processed by RouteParamsFactory before calling a method.
export const Request: () => ParameterDecorator = createRouteParamDecorator(
RouteParamtypes.REQUEST,
);
So Request is not retrieved by the #Req decorator itself. It only asks the NestJS framework to fill the annotated method parameter with reference of Request before calling the method.
BTW I also struggling with the same problem as you. I also was looking for a solution on how to access ExecutionContext from decorators. But decorators can access only annotated targets (classes, handlers, arguments, ...)
I think ExecutionContext only can be accessed directly from:
pipes
guards
interceptors
or from argument decorators this way:
https://docs.nestjs.com/custom-decorators#param-decorators
import { createParamDecorator, ExecutionContext } from '#nestjs/common';
export const User = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
},
);
NOTE: You can find source of createParamDecorator() in create-route-param-metadata.decorator.ts .

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 with fetch-mock generating error: TypeError: Cannot read property 'prototype' of undefined when using on nodejs

I'm almost sure it's my mistake, but I spent one entire day trying to solve and I've being failed 😞.
I'm trying to configure the fetch-mock on node to use with jest. I tried a lot of things and the best result that I have is this:
https://user-images.githubusercontent.com/824198/50566568-7b49e400-0d22-11e9-884f-89720899de3a.png
I'm sure my mock is working because if I pass it through parameter to the "Myclass.query" it works perfectly.
I'm also sure that the mock is arriving in my test file, because the mock function is present in the fetch module.
But... all together aren't working 😭.
I created a very simple and small project to see this problem happening:
https://github.com/cesarjr/test-node-fetch
Can anyone help me?
Jest uses the mock at __mocks__/node-fetch.js any time node-fetch is required during the test.
The problem is that the first thing fetch-mock does is require node-fetch.
This means that Request is not defined when it is set on the config here, so calling prototype on the undefined Request causes an error here.
Someone smarter than me might know how to force Jest to require the actual node-fetch when fetch-mock requires node-fetch in the mock for node-fetch, but from what I can see it doesn't look like it is possible.
Looks like you will have to delete the mock at __mocks__/node-fetch.js and pass fetch to your code, something like this:
myclass.js
class MyClass {
static query(fetch, sessionId, query) {
const url = 'https://api.foobar.com';
const body = {
sessionId,
query
};
return fetch(url, {
method: 'post',
body: JSON.stringify(body)
})
.then(res => res.json());
}
}
module.exports = MyClass;
...then create the sandbox in your test and pass it to your code, something like this:
myclass.test.js
const fetch = require('fetch-mock').sandbox();
const MyClass = require('./myclass');
describe('MyClass', () => {
describe('.query', () => {
it('returns the response', () => {
fetch.mock('*', {'result': {'fulfillment': {'speech': 'The answer'}}});
expect.assertions(1);
return MyClass.query(fetch, '123', 'the question').then((data) => {
expect(data.result.fulfillment.speech).toBe('The answer'); // SUCCESS
});
});
});
});
I've now found a reliable way to combine fetch-mock and jest http://www.wheresrhys.co.uk/fetch-mock/#usageusage-with-jest

Assert arguments of stubbed methods with sinon

import ManagerDaoStub from '../salesforce/__test__/ManagerDaoStub';
import criticalMerchants from '../criticalMerchants';
describe('criticalMerchants Unit Tests', () => {
before(() => {
ManagerDaoStub.initStubs();
});
after(() => {
ManagerDaoStub.restoreStubs();
});
it('assert the arguments of stubbed method', (done)=>{
let load = criticalMerchants.createCases(MERCHANT, DEVICE_ID, KEY, {});
return done();
});
})
This is the test file written in node criticalMerchants.test.js. The method i want to test which is createCases uses a method in ManagerDao, which has been stubbed in ManagerDaoStub as below.
import ManagerDao from '../ManagerDao';
class ManagerDaoStub {
constructor() {
this.sandbox = sinon.sandbox.create();
}
initStubs(sandbox) {
this.sandbox = sandbox || this.sandbox;
this.restoreStubs();
this.initFindOpenCases();
}
restoreStubs() {
this.sandbox.restore();
}
initFindOpenCases() {
let findOpenCases = this.sandbox.stub(ManagerDao, "findOpenCases");
findOpenCases
.withArgs(DEVICE_ID, KEY, match.func)
.callsArgWith(2, new Error("Test error"));
}
}
I want to assert whether this stubbed method initFindOpenCases was called with the right arguments (DEVICE_ID,KEY,null). I used
sinon.assert.calledWith(ManagerDaoStub.initFindOpenCases, DEVICE_ID, KEY, null) and this gives the following error:
AssertError: initFindOpenCases() is not stubbed.
Can someone suggest a proper way to do this?
First off, if ManagerDao.initFindOpenCases is an instance method (I'm unsure since you haven't shared its definition), then you can't stub it on the constructor like you've done here:
let findOpenCases = this.sandbox.stub(ManagerDao, "findOpenCases")
You need to either create an instance first-- then stub it on that instance-- or stub it on the prototype itself like so:
let findOpenCases = this.sandbox.stub(ManagerDao.prototype, "findOpenCases");
Secondly, you're making the same mistake again in your assertion, combined with another:
sinon.assert.calledWith(ManagerDaoStub.initFindOpenCases, DEVICE_ID, KEY, null)
ManagerDaoStub is the constructor, and it does not have an initFindOpenCases property. Its prototype does, and thus its instances do as well. On top of that, ManagerDaoStub.prototype.initFindOpenCases is still not a stub. It's a method you're calling to create a stub, but it is not itself a stub. More plainly, you're getting ManagerDao mixed up with ManagerDaoStub.
Assuming you make example change above, you can make your assertion work like this:
sinon.assert.calledWith(ManagerDao.prototype.initFindOpenCases, DEVICE_ID, KEY, null)
However, this isn't all I would recommend changing. This latter mixup is arising largely because you're vastly over-complicating the setup code of your test. You don't need to make an entire class to stub one method of ManagerDao.
Instead, just replace your before and after calls with these:
beforeEach(() => {
// Create the stub on the prototype.
sinon.stub(ManagerDao.prototype, 'findOpenCases')
.withArgs(DEVICE_ID, KEY, sinon.match.func)
.callsArgWith(2, newError('Test Error'));
});
afterEach(() => {
// As of sinon 5.0.0, the sinon object *is* a sandbox, so you can
// easily restore every fake you've made like so:
sinon.restore();
});
Aside from that, I recommend looking deeply into the difference between properties on a constructor and properties on its prototype. That knowledge will make stuff like this much easier for you. Best place to start is probably here on MDN.

Resources