mock a toPromise function in jest - got .toPromise is not a function - node.js

I have an httpService from nestjs/common
and I am using like the following:
const response = await this.httpService.post(`${this.api}/${action}`, data).toPromise();
and in my jest spec file ( unit testing) . i am trying to mock this service
httpServiceMock = {
post: jest.fn()
};
it('should start', async () => {
const serviceResult = await service.start(data);
});
and I have got this error :
TypeError: this.httpService.post(...).toPromise is not a function
I am also trying to add a promise result like :
const promise = Promise.resolve('result');
httpServiceMock.post.mockResolvedValue(promise);
tried also :
it('should start', async () => {
const mockObservable = Promise.resolve({
toPromise: () => {
console.log('toPromise called');
}
})
httpServiceMock.post.mockImplementation(() => mockObservable);
const serviceResult = await service.start();
});
My question is how can I mock the promise and return a response or exception

The return value httpService.post needs to return an Observable<AxiosResponse<T>> which includes a property toPromise, which is a function. Your mock returns a resolved promise, whereas it needs to return a fake Observable.
The Observable is returned immediately, so the post implementation can just return a raw value, but the toPromise needs to return a promise.
Return the correct shaped object to get rid of this error:
const mockObservable = {
toPromise: () => Promise.resolve('result')
}
httpServiceMock.post.mockImplementation(() => mockObservable);

I had a similar problem that could not be solved by accepted answer. So I bring here another solution just in case it could help someone else.
If you have jasmine, just use jasmine.createSpyObj(). If not, here is what I needed to do :
First, I implemented a jasmine.createSpyObj() equivalent (based on this answer with little modifications) :
export class TestUtilsService {
static createSpyObj (baseName:string, methodNames:string[]): SpyObject {
let obj: any = {};
for (let i = 0; i < methodNames.length; i++) {
obj[methodNames[i]] = jest.fn();
}
return {[baseName]:()=>obj};
};
}
export class SpyObject {
[key: string]: ()=>{[key:string]:jest.Mock} ;
}
Then I used it in my unit test :
const spyHttpClient: SpyObject = TestUtilsService.createSpyObj('get',['toPromise']);
Add it in test module providers :
{provide: HttpClient, useValue: spyHttpClient}
Finally, mock the toPromise implementation in order to return a mocked response :
const mockedResponse = {...};
spyHttpClient.get().toPromise.mockImplementationOnce(()=>Promise.resolve(mockedResponse));
await service.myRealMethodThatCallsHttpClient();
expect(service.someUpdatedProp).toBeTruthy();
Please notice parenthesis after method get.

"A Jar of Clays" solution didn't work for me (I got mockImplementation is not a function), but this worked:
const mockPromise = {
toPromise: () => Promise.resolve(ical)
}
mockHttpService.get = jest.fn( () => {return mockPromise});

Related

how to mock react-query useQuery in jest

I'm trying to mock out axios that is inside an async function that is being wrapped in useQuery:
import { useQuery, QueryKey } from 'react-query'
export const fetchWithAxios = async () => {
...
...
...
const response = await someAxiosCall()
...
return data
}
export const useFetchWithQuery = () => useQuery(key, fetchWithAxios, {
refetchInterval: false,
refetchOnReconnect: true,
refetchOnWindowFocus: true,
retry: 1,
})
and I want to use moxios
moxios.stubRequest('/some-url', {
status: 200,
response: fakeInputData,
})
useFetchWithQuery()
moxios.wait(function () {
done()
})
but I'm getting all sorts of issues with missing context, store, etc which I'm iterested in mocking out completely.
Don't mock useQuery, mock Axios!
The pattern you should follow in order to test your usages of useQuery should look something like this:
const fetchWithAxios = (axios, ...parameters) => {
const data = axios.someAxiosCall(parameters);
return data;
}
export const useFetchWithQuery = (...parameters) => {
const axios = useAxios();
return useQuery(key, fetchWithAxios(axios, ...parameters), {
// options
})
}
Where does useAxios come from? You need to write a context to pass an axios instance through the application.
This will allow your tests to look something like this in the end:
const { result, waitFor, waitForNextUpdate } = renderHook(() => useFetchWithQuery(..., {
wrapper: makeWrapper(withQueryClient, withAxios(mockedAxios)),
});
await waitFor(() => expect(result.current.isFetching).toBeFalsy());

Jest - mock function inside another module function

I'm trying to mock the return value (or implementation) of the functions inside another module's function with Jest. I need to test different scenarios (function throws error, returns null, returns an object, etc...)
That module (userService) returns a function that returns an object with that functions:
userService.js (I want to mock the return value of findUser & createUser)
...
function userService(userModel) {
async function findUser(userQuery) {
...
return foundUser;
}
async function createUser(user) {
...
return createdUser;
}
return { findUser, createUser };
}
module.exports = userService;
And I'm testing authStravaController, which uses that service functions:
authStravaController
...
const authStravaServiceRaw = require('../../services/authStravaService');
const userServiceRaw = require('../../services/userService');
const bikeServiceRaw = require('../../services/bikeService');
...
function authStravaController(userModel, bikeModel) {
const { findUser, createUser } = userServiceRaw(userModel); <-- WANT TO MOCK THAT FUNCTIONS
async function authStrava({ body: { authCode } }, res) {
...
try {
...
const findUserQuery = {
stravaUserId: stravaBasicUser.stravaUserId,
};
authUser = await findUser(findUserQuery); <-- MOCK THIS FUNCTION RETURN MULTIPLE TIMES
if (!authUser) {
resStatus = CREATED;
createdUser = await createUser(stravaBasicUser); <-- SAME
...
createdUser.bikes = createdBikes.map((bike) => bike._id);
createdUser.save();
authUser = { createdUser, createdBikes };
}
return handleResponseSuccess(res, resStatus, authUser);
} catch (authStravaError) {
return handleResponseError(res, authStravaError);
}
}
return { authStrava };
}
module.exports = authStravaController;
At the moment I've been able to mock the function return value just 1 time, and I can't find a way to rewrite it, so now I can only test 1 scenario
This code at the top of the file let me test 1 scenario
jest.mock('../../services/userService', () => () => ({
findUser: jest.fn().mockReturnValue(1),
createUser: jest.fn().mockReturnValue({ username: 'userName', save: jest.fn() }),
}));
I've tried to mock it in multiple ways and can't get it to work, how could I do it to test different return values:
findUser: jest.fn().mockReturnValue(1),
findUser: jest.fn().mockReturnValue(undefined),
findUser: jest.fn().mockReturnValue({user:'username'}),
etc...
Thanks!
I fixed it importing all the services outside the controller function, at the top of the file.
This way I can mock the returnValue of any function.

How to test function in class using jest

I wasn't able to make unit testing worked using jest
I'm trying to test a specific function that's calling or expecting result from other function but I'm not sure why it is not working. I'm pretty new to unit testing and really have no idea how could I make it work. currently this is what I've tried
export class OrganizationService {
constructor() {
this.OrganizationRepo = new OrganizationRepository()
}
async getOrganizations(req) {
if (req.permission !== 'internal' && req.isAuthInternal === false) {
throw new Error('Unauthenticated')
}
const opt = { deleted: true }
return this.OrganizationRepo.listAll(opt)
}
}
This is my OrganizationRepository that extends the MongoDbRepo
import { MongoDbRepo } from './mongodb_repository'
export class OrganizationRepository extends MongoDbRepo {
constructor(collection = 'organizations') {
super(collection)
}
}
and this is the MongoDbRepo
const mongoClient = require('../config/mongo_db_connection')
const mongoDb = require('mongodb')
export class MongoDbRepo {
constructor(collectionName) {
this.collection = mongoClient.get().collection(collectionName)
}
listAll(opt) {
return new Promise((resolve, reject) => {
this.collection.find(opt).toArray((err, data) => {
if (err) {
reject(err)
}
resolve(data)
})
})
}
}
and this is the test that I've made
import { OrganizationService } from '../../../src/services/organization_service'
describe('getOrganizations', () => {
it('should return the list of organizations', () => {
// const OrgRepo = new OrganizationRepository()
const orgService = new OrganizationService()
const OrgRepo = jest.fn().mockReturnValue("[{_id: '123', name: 'testname'}, {_id: '456, name: 'testname2'}]")
// orgService.getOrganizations = jest.fn().mockReturnValue('')
const result = orgService.getOrganizations()
expect(result).toBe(OrgRepo)
})
})
I see two issues in the way you are testing:
1.
You are trying to test an asynchronous method, and on your test, you are not waiting for this method to be finished before your expect statement.
A good test structure should be:
it('should test your method', (done) => {
const orgService = new OrganizationService();
const OrgRepo = jest.fn().mockReturnValue("[{_id: '123', name: 'testname'}, {_id: '456, name: 'testname2'}]")
orgService.getOrganizations()
.then((result) => {
expect(result).toEqual(OrgRepo); // I recommend using "toEqual" when comparing arrays
done();
});
})
Don't forget to put done as a parameter for your test!
You can find more about testing asynchronous functions on the Jest official documentation.
2.
In order to test your method properly, you want to isolate it from external dependencies. Here, the actual method OrganizationRepo.listAll() is called. You want to mock this method, for instance with a spy, so that you control its result and only test the getOrganizations method. That would look like this:
it('should test your method', (done) => {
const req = {
// Whatever structure it needs to be sure that the error in your method is not thrown
};
const orgService = new OrganizationService();
const orgRepoMock = spyOn(orgService['OrganizationRepo'], 'listAll')
.and.returnValue(Promise.resolve("[{_id: '123', name: 'testname'}, {_id: '456, name: 'testname2'}]"));
const OrgRepo = jest.fn().mockReturnValue("[{_id: '123', name: 'testname'}, {_id: '456, name: 'testname2'}]");
orgService.getOrganizations(req)
.then((result) => {
expect(result).toEqual(OrgRepo); // I recommend using "toEqual" when comparing arrays
expect(orgRepoMock).toHaveBeenCalled(); // For good measure
done();
});
})
This way, we make sure that your method is isolated and its outcome cannot be altered by external methods.
For this particular method, I also recommend that you test the error throwing depending on the input of your method.
I was able to answer this, first is I mocked the repository using
jest.mock('path/to/repo')
const mockGetOne = jest.fn()
OrganizationRepository.protorype.getOne = mockGetOne
then the rest is the test

sinon.spy in my Node.JS project when testing an AWS service not working as expected

in my Node.JS project (a backend for an Angular 5 project) I have created a service that deals with the AWS Authentication... I have called this awsAuthenticationService. All works well but I now need to test it. In my awsAuthenticationService.js I have the following method that has some minor logic and then calls a method provided by the "cognitoIdentityServiceProvider". Here is a snippet of my code (I really have reduced this)
constructor() {
this._cognitoIdentityServiceProvider = new AWS.CognitoIdentityServiceProvider(this.cognitoConfig);
}
toggleUserAccess(userName, type) {
const params = {
Username: userName,
UserPoolId: this.cognitoConfig.userPoolId
};
if (type === null) {
return this._cognitoIdentityServiceProvider.adminEnableUser(params).promise();
}
return this._cognitoIdentityServiceProvider.adminDisableUser(params).promise();
}
As you can see from the toggleUserAccess we pass a few parameters, determine what they are then call the appropriate method. I wish to test this by having a unit test that will call the authenticationService.toggleUserAccess, pass some params and spy on the authenticationService._cognitoIdentityServiceProvider methods to see if they were called. I set it up so...
let authenticationService = require('./awsAuthenticationService');
describe('toggleUserAccess', () => {
beforeEach(() => {
authenticationService._cognitoIdentityServiceProvider = {
adminDisableUser(params) {
return {
promise() {
return Promise.resolve(params);
}
};
}
};
authenticationService._cognitoIdentityServiceProvider = {
adminEnableUser(params) {
return {
promise() {
return Promise.resolve(params);
}
};
}
};
});
it('should call adminEnableUser if the type is null', () => {
authenticationService.toggleUserAccess('TheUser', null);
const spyCognito = sinon.spy(authenticationService._cognitoIdentityServiceProvider, 'adminEnableUser');
expect(spyCognito.calledOnce).to.equal(true);
});
it('should call adminDisableUser if the type is null', () => {
authenticationService.toggleUserAccess('TheUser', '0001');
const spyCognito = sinon.spy(authenticationService._cognitoIdentityServiceProvider, 'adminDisableUser');
expect(spyCognito.calledOnce).to.equal(true);
});
});
My tests aren't passing and I think I have set up my sinon.spys incorrectly - can anyone see what I am doing wrong or give advice please
To stub class of AWS.CognitoIdentityServiceProvider, need to stub with its prototype keyword.
// add require statement for your AWS class
const spyCognito = sinon.spy(AWS.CognitoIdentityServiceProvider.prototype, 'adminDisableUser');
expect(spyCognito.calledOnce).to.equal(true);
Hope it helps

Mock.mockImplementation() not working

I have a service class
Service.js
class Service {
}
export default new Service();
And I am trying to provide a mock implementation for this. If I use something like this:
jest.mock('./Service', () => { ... my mock stuff });
It works fine, however I'm not able to access any variables declared outside of the mock, which is a bit limiting as I'd like to reconfigure what the mock returns, etc.
I tried this (inspired by this other StackOverflow article: Service mocked with Jest causes "The module factory of jest.mock() is not allowed to reference any out-of-scope variables" error)
import service from './Service';
jest.mock('./Service', () => jest.fn);
service.mockImplementation(() => {
return { ... mock stuff }
);
Unfortunately when I am trying to run this, I get the below error:
TypeError: _Service2.default.mockImplementation is not a function
I had same problem as #Janos, the other answers didn't help either. You could do two things :
If you need to mock only a function from Service, in your test file:
import service from './Service';
jest.mock('./Service', () => jest.fn());
service.yourFunction = jest.fn(() => { /*your mock*/ })
 
If you need to mock the entire Module:
Say your service.js is in javascript/utils, create a javascript/utils/_mocks_ and inside it create a service.js file, you can then mock the entire class in this file, eg:
const myObj = {foo: "bar"}
const myFunction1 = jest.fn(() => { return Promise.resolve(myObj) })
const myFunction2 = ...
module.exports = {
myFunction1,
myFunction2
}
then in your test file you just add:
jest.mock('./javascript/utils/service')
...functions exported from the mockfile will be then hit through your test file execution.
The mock is equal to jest.fn. You need to call jest.fn to create a mocked function.
So this:
jest.mock('./Service', () => jest.fn);
Should be:
jest.mock('./Service', () => jest.fn());
ran into similar issues and resolved it by using .mockImplementationOnce
jest.mock('./Service', () => jest.fn()
.mockImplementationOnce(() => {
return { ... mock stuff }
})
.mockImplementationOnce(() => {
return { ... mock other stuff }
})
);
now when you run another test it will return the second mock object.
You need to store your mocked component in a variable with a name prefixed by "mock" and make sure you return an object with a default property as you import your Service from the default in your "main.js" file.
// Service.js
class Service {
}
export default new Service();
// main.test.js (main.js contains "import Service from './Service';")
const mockService = () => jest.fn();
jest.mock('./Service', () => {
return {
default: mockService
}
});
I had similar problem, and the cause was that ".spec.js" file had an
import jest from "jest-mock";
After removing this line, it worked.
My mistake was that I was resetting the mock before each test. If you do that, be sure to reconfigure the mock implementation.
For example, change this:
let value;
let onPropertyChange: OnPropertyChangeCallback = jest.fn((changes: any) => {
value = changes["testValue"];
});
const user = userEvent.setup();
beforeEach(() => {
jest.resetAllMocks();
});
to this:
let value;
let onPropertyChange: OnPropertyChangeCallback;
const user = userEvent.setup();
beforeEach(() => {
jest.resetAllMocks();
onPropertyChange = jest.fn((changes: any) => {
value = changes["testValue"];
});
});

Resources