jest testing of method using native module - javascript

I am trying to create an unit test for my function.
Below the method "requestAuthorization" is a method in swift and i am invoking it through RN's Native Modules.
I get this error "Cannot read property 'requestAuthorization' of undefined"
If the method runs successfully it resolves back a string "Authorized Successfully!"
How can i test this????
index.ts (my method "requestAuthorization" is defined here)
const requestAuthorization = (
read: [],
write: [] = []
): Promise<boolean> => {
const readAuth = read.reduce((obj, cur) => {
return { ...obj, [cur]: true };
}, {});
const writeAuth = write.reduce((obj, cur) => {
return { ...obj, [cur]: true };
}, {});
return NativeModules.MyHealthLibrary.requestAuthorization(writeAuth, readAuth);
};
const HealthKit = {
requestAuthorization
}
export default HealthKit;
Now in my test.js
import { type } from '../authTypes';
import { NativeModules } from 'react-native';
jest.mock('react-native', () => {
return {
NativeModules: {
MyHealthLibrary: {
requestAuthorization: jest.fn(() => Promise.resolve())
}
}
}
})
const utils = jest.createMockFromModule('../index.ts').default;
utils.requestAuthorization = jest.fn((read, write) => {
return NativeModules.MyHealthLibrary.requestAuthorization(read, write)
});
test('healthkit permissions', () => {
expect(utils.requestAuthorization.mock).toBeTruthy();
let read = [type.heartRate]
let write = [type.heartRate]
expect(read).toEqual(["HKQuantityTypeIdentifierHeartRate"])
expect(write).toEqual(["HKQuantityTypeIdentifierHeartRate"])
await utils.requestAuthorization(read, write).then(result => {
console.log("result:",result) //ALWAYS UNDEFINED
})
});
UPDATE:
In my swift method "requestAuthorization", it resolves a string "Authorized Successfully!".
jest.mock('../node_modules/react-native/Libraries/BatchedBridge/NativeModules', () => ({
MyHealthLibrary: {
requestAuthorization: jest.fn(() => Promise.resolve())
},
}));
test('healthkit permissions', async() => {
let read = [type.heartRate]
let write = [type.heartRate]
expect(read).toEqual(["HKQuantityTypeIdentifierHeartRate"])
expect(write).toEqual(["HKQuantityTypeIdentifierHeartRate"])
let result = await NativeModules.MyHealthLibrary.requestAuthorization(read, write)
console.log("result:",result)
//NOT PRINTING "Authorized Successfully!" INSTEAD PRINTING "undefined"
});

Related

When I log Array there's an object inside, but when I'm trying to access that object it returns me undefined

This is my cache "component":
// imports
const useCache = (cacheName: string, url: string) => {
const cacheArray: Array<Object> = []
const getAllCaches = async () => {
const cacheNames = await caches.keys();
for (const cname of cacheNames) {
const cacheStorage = await caches.open(cname);
const cachedResponse = await cacheStorage.match(url);
const cdata = await cachedResponse?.json()
cacheArray.push({name: cname, data: cdata})
}
}
useEffect(() => {
getAllCaches()
.catch(err => console.log(err))
}, [])
const addCache = (response: any) => {
const data = new Response(JSON.stringify(response));
if ('caches' in window) {
caches.open(cacheName).then((cache) => {
cache.put(url, data);
});
}
const finalData = {name: cacheName, data: response}
cacheArray.push(finalData)
return data
}
const getCache = (cacheName?: string) => {
if (cacheName) {
return cacheArray.filter((i: any) => i.name === cacheName)[0]
}
else {
return cacheArray
}
}
const removeCache = (cacheName: string) => {
caches.delete(cacheName).then(function (res) {
return res;
});
}
return [
getCache as (cacheName?: any) => any,
addCache as (response: any) => any,
removeCache as (cacheName: any) => any
]
};
export default useCache;
Now here's code in my home component:
const [getCache, addCache, removeCache] = useCache("user", "http://localhost:3000")
useEffect(() => {
console.log(getCache())
console.log(getCache()[0])
console.log(getCache().length)
// the rest of code, not matter
and when I run home component (with vite and preact) it logging me Array, then unfedinfed, then 0 (but second should return object, and third should return 1) also I attached a screen from console.
Why it's returning me undefined and 0 length when it should return object and 1?
I'm using preact, vite, newest nodejs, typescript

MockReturnValue is returning undefined on axios wrapper function

I am trying to test my componentDidMount and ensure that this axiosRequest returns an array. However no matter how I mock my axiosRequest function, it always returns undefined. What am I missing??
My axiosRequest wrapper fn:
export const axiosRequest = (type, url, body, headers) => {
return axios[type](url,
{
...body,
sharedLinkToken: store.getState()?.token ? store.getState()?.token : null,
activeWorkspace: body?.activeWorkspace ? body?.activeWorkspace :
store.getState()?.auth?.org?.activeWorkspace,
},
{ ...headers },
).then((res) => res?.data);
};
Calling said function in my CDM
async componentDidMount() {
try {
const fieldTypes = await axiosRequest('post', '/api/custom-fields/types');
console.log('fieldTypes: ', fieldTypes);
this.setState({ fieldTypes });
} catch (e) {
this.setState({ disable: true });
}
}
My test with its imports with my various ways to mock this function:
import * as axiosRequest from '../../../../utilities/utilities';
let mockAxiosRequest;
// jest.mock('axios');
// jest.mock('../../../../utilities/utilities', () => ({
// axiosRequest: jest.fn(),
// }));
beforeEach(() => {
props = {};
wrapper = shallow(<CreateCustomField {...props} />);
instance = wrapper.instance();
mockAxiosRequest = jest.spyOn(axiosRequest, 'axiosRequest');
});
it('Should find the wrapper', async () => {
mockAxiosRequest.mockResolvedValue(['stuff']);
// mockAxiosRequest.mockResolvedValue(() => ['stuff']);
// mockAxiosRequest.mockResolvedValueOnce(['stuff']);
// mockAxiosRequest.mockReturnThis(['stuff']);
// mockAxiosRequest.mockImplementation(() => ['stuff']);
// mockAxiosRequest.mockImplementation(() => Promise.resolve(['stuff']));
expect(wrapper.find('.create-custom-fields-modal').length).toBe(1);
expect(instance.state.fieldTypes.length).toBe(1);
});
I found that my mocks were working fine, however the componentDidMount was the issue. So because of this, I am doing everything manually, and everything is working as expected.
beforeEach(() => {
props = {};
mockAxiosRequest = jest.spyOn(axiosRequest, 'axiosRequest');
wrapper = shallow(<CreateCustomField {...props} />, { disableLifecycleMethods: true });
instance = wrapper.instance();
instance.setState({ fieldTypeOptions: [{ persistent_id: '123',field_type: 'Date & Time' }] });
});
Then my two tests that are now passing:
it('Should find the wrapper', async () => {
expect(wrapper.find('.create-custom-fields-modal').length).toBe(1);
});
it('Should call componentDidMount', async () => {
mockAxiosRequest.mockResolvedValueOnce([
{ persistent_id: '456', field_type: 'Date & Time' },
{ persistent_id: '123', field_type: 'Number' },
]);
await instance.componentDidMount();
expect(instance.state.fieldTypeOptions.length).toBe(2);
});

How to access a mock method returned from a mocked library

I'm mocking the #elastic/elasticsearch library and I want to test that the search method is called with the right arguments but I'm having issues accessing search from my tests.
In my ES mock I just export an object that includes a Client prop that returns another object that has the search prop. This is the way search is accessed from the library
const { Client } = require('#elastic/elasticsearch')
const client = new Client(...)
client.search(...)
__mocks__/#elastic/elasticsearch
module.exports = {
Client: jest.fn().mockImplementation(() => {
return {
search: (obj, cb) => {
return cb(
'',
{
statusCode: 200,
body: {
hits: {
hits: [
{
_source: esIndexes[obj.index]
}
]
}
}
}
)
}
}
})
}
__tests__/getAddresses.test.js
const { getAddresses } = require('../src/multiAddressLookup/utils/getAddresses')
const { Client } = require('#elastic/elasticsearch')
beforeEach(() => {
process.env.ES_CLUSTER_INDEX = 'foo'
process.env.ui = '*'
})
describe('multiAddressLookup', () => {
test('Should return the correct premises data with only the relevant "forecasted_outages"', async () => {
const event = {
foo: 'bar'
}
const esQueryResponse = {
"body": "\"foo\":\"bar\"",
"headers": {"Access-Control-Allow-Origin": '*'},
"statusCode": 200
}
await expect(getAddresses(event)).resolves.toEqual(esQueryResponse)
expect(Client().search).toHaveBeenCalled() // This fails with 0 calls registered
})
})
I'm not sure of any exact documentation for this scenario but I got the idea while looking through the Jest: The 4 ways to create an ES6 Mock Class - Automatic mock portion of the Jest documentation.
First, the search method in the ES mock, __mocks__/#elastic/elasticsearch, needs to be converted into a jest mock function, jest.fn(). Doing this gives us access to properties and values that jest mocks provide.
__mocks__/#elastic/elasticsearch.js converted
module.exports = {
Client: jest.fn().mockImplementation(() => {
return {
search: jest.fn((obj, cb) => {
return cb(
'',
{
statusCode: 200,
body: {
hits: {
hits: [
{
_source: esIndexes[obj.index]
}
]
}
}
}
)
})
}
})
}
Second, in our tests we need to follow the path from the Client mock class until we find out methods. The syntax is MockClass.mock.results[0].value.mockFunction.
Example Test
const { Client } = require('#elastic/elasticsearch') // This is located in the "__mocks__" folder in the root of your project
const { getAddresses } = require('../../src/getAddresses') // This is the file we wrote and what we are unit testing
describe('getAddresses', () => {
it('Should call the ES Search method', async () => {
const event = { ... }
const expected = { ... }
await expect(getAddresses(event)).resolves.toEqual(expected) // pass
expect(Client.mock.results[0].value.search).toHaveBeenCalled() // pass
})
})

How to test axios get request function in jest/enzyme tests?

I have follow React component which render as a child in parent component and props are passed:
<Component
localStorageValue={'test-walue'}
requestDataFunc={getData}
requestUserData={getUserData}
expectedResponseKey={'key'}
dataType={'test}
activePage={'index'}
saveData={this.setData}
/>
so requestDataFunc is a funtion which passed to component and runned in componentDidMount :
componentDidMount() {
requestDataFunc().then(({ data }) => {
const { selectedDataItems } = this.state;
const expectedResponseData = data[expectedResponseKey];
let interimDataArr = [];
expectedResponseData.forEach((item) => {
interimDataArr = [...interimDataArr, {
...item,
active: selectedDataItems.length ? selectedDataItems.some((selectedItemId) => selectedItemId === item.id) : false,
}];
});
}
but when I run my tests, I got:
TypeError: Cannot read property 'then' of undefined
requestDataFunc().then(({ data }) => {
const { selectedDataItems } = this.state;
const expectedResponseData = data[expectedResponseKey];
let interimDataArr = [];
I just starting to test render component:
describe('correct component render', () => {
const defaultProps = {
localStorageValue: 'test-walue',
requestDataFunc: jest.fn(),
requestUserData: jest.fn(),
expectedResponseKey: 'key',
dataType: 'test',
activePage: 'index',
saveData: jest.fn(),
};
const wrapper = shallow(<ComponentName { ...defaultProps } />);
test("render component", () => {
expect(wrapper.length).toEqual(1);
});
});
I suppose that I need to mock somehow request and data that this request should receive. How to do this correctly?
Have you tried mocking promise as below:
var mockPromise = new Promise((resolve, reject) => {
resolve(<mock response similar to actual promise response>);
});
describe('correct component render', () => {
const defaultProps = {
localStorageValue: 'test-walue',
requestDataFunc: jest.fn().mockReturnValueOnce(mockPromise),
requestUserData: jest.fn(),
expectedResponseKey: 'key',
dataType: 'test',
activePage: 'index',
saveData: jest.fn(),
};
const wrapper = shallow(<ComponentName { ...defaultProps } />);
test("render component", () => {
expect(wrapper.length).toEqual(1);
});
});
Axios get method returns a promise, so you when you mock that method you also need to return a Promise
jest.fn(() => Promise.resolve({}))
More on Jest async mocking
Relevant answer from SO

action creator does not return value to stream in marble test

I've got following Epic which works well in application, but I can't get my marble test working. I am calling action creator in map and it does return correct object into stream, but in the test I am getting empty stream back.
export const updateRemoteFieldEpic = action$ =>
action$.pipe(
ofType(UPDATE_REMOTE_FIELD),
filter(({ payload: { update = true } }) => update),
mergeMap(({ payload }) => {
const { orderId, fields } = payload;
const requiredFieldIds = [4, 12]; // 4 = Name, 12 = Client-lookup
const requestData = {
id: orderId,
customFields: fields
.map(field => {
return (!field.value && !requiredFieldIds.includes(field.id)) ||
field.value
? field
: null;
})
.filter(Boolean)
};
if (requestData.customFields.length > 0) {
return from(axios.post(`/customfields/${orderId}`, requestData)).pipe(
map(() => queueAlert("Draft Saved")),
catchError(err => {
const errorMessage =
err.response &&
err.response.data &&
err.response.data.validationResult
? err.response.data.validationResult[0]
: undefined;
return of(queueAlert(errorMessage));
})
);
}
return of();
})
);
On successfull response from server I am calling queueAlert action creator.
export const queueAlert = (
message,
position = {
vertical: "bottom",
horizontal: "center"
}
) => ({
type: QUEUE_ALERT,
payload: {
key: uniqueId(),
open: true,
message,
position
}
});
and here is my test case
describe("updateRemoteFieldEpic", () => {
const sandbox = sinon.createSandbox();
let scheduler;
beforeEach(() => {
scheduler = new TestScheduler((actual, expected) => {
expect(actual).toEqual(expected);
});
});
afterEach(() => {
sandbox.restore();
});
it("should return success message", () => {
scheduler.run(ts => {
const inputM = "--a--";
const outputM = "--b--";
const values = {
a: updateRemoteField({
orderId: 1,
fields: [{ value: "test string", id: 20 }],
update: true
}),
b: queueAlert("Draft Saved")
};
const source = ActionsObservable.from(ts.cold(inputM, values));
const actual = updateRemoteFieldEpic(source);
const axiosStub = sandbox
.stub(axios, "post")
.returns([]);
ts.expectObservable(actual).toBe(outputM, values);
ts.flush();
expect(axiosStub.called).toBe(true);
});
});
});
output stream in actual returns empty array
I tried to return from map observable of the action creator which crashed application because action expected object.
By stubbing axios.post(...) as [], you get from([]) in the epic - an empty observable that doesn't emit any values. That's why your mergeMap is never called. You can fix this by using a single-element array as stubbed value instead, e.g. [null] or [{}].
The below is an answer to a previous version of the question. I kept it for reference, and because I think the content is useful for those who attempt to mock promise-returning functions in epic tests.
I think your problem is the from(axios.post(...)) in your epic. Axios returns a promise, and the RxJS TestScheduler has no way of making that synchronous, so expectObservable will not work as intended.
The way I usually address this is to create a simple wrapper module that does Promise-to-Observable conversion. In your case, it could look like this:
// api.js
import axios from 'axios';
import { map } from 'rxjs/operators';
export function post(path, data) {
return from(axios.post(path, options));
}
Once you have this wrapper, you can mock the function to return a constant Observable, taking promises completely out of the picture. If you do this with Jest, you can mock the module directly:
import * as api from '../api.js';
jest.mock('../api.js');
// In the test:
api.post.mockReturnValue(of(/* the response */));
Otherwise, you can also use redux-observable's dependency injection mechanism to inject the API module. Your epic would then receive it as third argument:
export const updateRemoteFieldEpic = (action$, state, { api }) =>
action$.pipe(
ofType(UPDATE_REMOTE_FIELD),
filter(({ payload: { update = true } }) => update),
mergeMap(({ payload }) => {
// ...
return api.post(...).pipe(...);
})
);
In your test, you would then just passed a mocked api object.

Categories

Resources