I've got an odd error where while using Jest spyOn and mocking a returned value, that object's value get returned as undefined.
import { Location as LocationAPI } from '../index'
import REST from '../../MafRestApi'
const mockCoordinatesArray = [{'longitude': 116.0654299154163, 'latitude': -33.85910894972095, 'altitude': 1182.5}]
it('Calls the Location Coordinates API and gets a response', () => {
jest.spyOn(REST, 'get').mockReturnValue(Promise.resolve(mockCoordinatesArray))
return LocationAPI.getLocationCoordinates()
.then((results) => {
expect(results).toEqual(mockCoordinatesArray)
})
})
The LocationAPI.getLocationCoordinates() methods looks like this:
getLocationCoordinates (): Promise<Array<LocationCoordinates>> {
return REST.get(`${ENDPOINTS.LOCATION}`)
.then((coordinates) => coordinates.map((coordinate) => new LocationCoordinates(coordinate)))
}
This the spyOn is successfully intercepting.
The output from Jest results in:
Error: expect(received).toEqual(expected)
Expected value to equal: [{"altitude": 1182.5, "latitude": -33.85910894972095, "longitude": 116.0654299154163}]
Received:[{"altitude": undefined, "latitude": undefined, "longitude": undefined}]
I've used exactly the same signature for another API call and it's working fine.
Why would the keys be kept intact where the values not?
There's a problem in your getLocationCoordinates. getLocationCoordinates is not returning the mapped coordinates in your code.
Try this:
getLocationCoordinates(): Promise<Array<LocationCoordinates>> {
return new Promise((resolve) => {
REST.get(`${ENDPOINTS.LOCATION}`).then((coordinates) => {
const mappedCoordinates = coordinates.map((coordinate) => new LocationCoordinates(coordinate))
resolve(mappedCoordinates);
});
});
}
or using async/await:
async getLocationCoordinates(): Promise<Array<LocationCoordinates>> {
const coordinates = await REST.get(`${ENDPOINTS.LOCATION}`);
return coordinates.map((coordinate) => new LocationCoordinates(coordinate));
}
Related
I have the below line in my service.
const timeline = await buildClient.getBuildTimeline(project.id, buildId);
The above code is successfully mocked by jest as below using the __mocks__ approach.
jest.mock('azure-devops-extension-api', () => {
return { getClient: (client: any) => new GitRestClient() };
});
export const mockGetItems = jest
.fn()
.mockReturnValue({ records: [{ state: 2, type: 'TASK', task: { id: TASK_IDS[0] } }] });
class GitRestClient {
public getBuildTimeline(projectId: string, buildId: string): Promise<Timeline[]> {
return new Promise((resolve) => resolve(mockGetItems()));
}
}
But when I get error during jest test in the below find method which acts upon the mocked response.
const record = timeline.records.find((rec) => {
return rec.type === 'Task';
});
How should I update so that the above code snippet is mocked perfectly to return the record value using the find method.
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());
I am using Cypress for my end to end Integration tests. I have a use case which involves returning a list of objects from Cypress Custom Commands and I have a difficulty in doing so. Here is my code pointer:
index.ts
declare global {
namespace Cypress {
interface Chainable<Subject> {
getTestDataFromElmoDynamoDB({locale, testType}): Cypress.Chainable<JQuery<expectedData[]>> // ??? not sure what return type should be given here.
}
}
}
Cypress.Commands.add('getTestDataFromDynamoDB', ({locale, testType}) => {
// expectedData is an interface declared. My use case is to return the list of this type.
let presetList: expectedData[]
cy.task('getTestDataFromDynamoDB', {
locale: locale,
testType: testType
}).then((presetData: any) => {
presetList = presetData;
// the whole idea here is to return presetList from cypress task
return cy.wrap(presetList) //??? not sure what should be written here
})
})
sampleSpec.ts
describe('The Sample Test', () => {
it.only('DemoTest', () => {
cy.getTestDataElmoDynamoDB({
locale: env_parameters.env.locale,
testType: "ChangePlan"
}).then((presetlist) => {
// not sure on how to access the list here. Tried wrap and alias but no luck.
presetList.forEach((preset: expectedData) => {
//blah blah blah
})
})
})
})
Did anyone work on similar use case before?
Thanks,
Saahith
Here My own command for doing exactly that.
Cypress.Commands.add("convertArrayOfAlliasedElementsToArrayOfInteractableElements", (arrayOfAlliases) => {
let arrayOfRecievedAlliasValues = []
for (let arrayElement of arrayOfAlliases) {
cy.get(arrayElement)
.then(aelement =>{
arrayOfRecievedAlliasValues.push(aelement)
})
}
return cy.wrap(arrayOfRecievedAlliasValues)
})
The way I do it is to pass it in an array and cy.wrap the array, Because it lets you chain the command with an interactable array.
The key point is - it has to be passed as array or object, because they are Reference types, and in cypress it is hard to work with let/var/const that are value types.
You can also allias the cy.wrapped object if you like.
The way to use it in code is:
cy.convertArrayOfAlliasedElementsToArrayOfInteractableElements(ArayOfElements)
What you asked for can be implemented as follows, but I do not know what type expectedData is, so let's assume that expectedData:string [], but you can replace string[] with your type.
plugins/index.ts
module.exports = (on: any, config: any) => {
on('task', {
getDataFromDB(arg: {locale: string, testType: string}){
// generate some data for an example
const list: string[] = [];
list.push('a', 'b');
return list;
},
});
};
commands.ts
declare global {
namespace Cypress {
interface Chainable<Subject> {
getTestDataElmoDynamoDB(arg: {locale: string, testType: string}): Cypress.Chainable<string[]>
}
}
}
Cypress.Commands.add('getTestDataElmoDynamoDB', (arg: {locale: string, testType: string}) => {
let presetList: string[] = [];
cy.task('getDataFromDB', arg)
.then((presetData?: string[]) => {
expect(presetData).not.be.undefined.and.not.be.empty;
// if the data is incorrect, the code will break earlier on expect, this line for typescript compiler
if (!presetData || !presetData.length) throw new Error('Present data are undefined or empty');
presetList = presetData;
return cy.wrap(presetList); // or you can return cy.wrap(presetData)
});
});
db.spec.ts
describe('Test database methods', () => {
it('When take some test data, expect that the data was received successfully ', () => {
cy.getTestDataElmoDynamoDB({ locale: 'someEnvVar', testType: 'ChangePlan' })
.then((list) => {
expect(list).not.empty.and.not.be.undefined;
cy.log(list); // [a,b]
// You can interact with list here as with a regular array, via forEach();
});
});
});
You can also access and receive data from cy.task directly in the spec file.
describe('Test database methods', () => {
it('When take some test data, expect that the data was received successfully ', () => {
cy.task('getDataFromDB', arg)
.then((list?: string[]) => {
expect(list).not.be.empty.and.not.be.undefined;
cy.log(list); // [a,b] — the same list as in the version above
});
});
});
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});
I have the following js function:
const modelUtils = {
modelingObj(obj, stringVal = ‘RE’) {
let newObj;
//my logic for setting value of newObj
return newObj;
};
export default modelUtils;
I want to test and see that based on a specific params I get a particular result, the issue is I’m always returning back an empty object.
Test.js
import modelUtils from '../modelUtils';
jest.unmock('../modelUtils');
describe(' testing modelUtils', () => {
let test;
const mockData = {
myProperty: [],
};
describe('testing modelingObj function', () => {
it('For my first test’, () => {
test = modelUtils.mockData, ‘TR’);
expect(test).toEqual({ myProperty: [] });
});
});
});