I am trying to write a unit test for my NodeJs code. I am mocking my API call by using mockResolvedValue.
This is my unit test:
const { search } = require("../src/utils");
jest.mock("axios");
test("Should find an user", async () => {
axios.get.mockResolvedValue({
data: [
{
userId: 1,
name: "Mike",
},
],
});
const phone = "123456789";
const token = "ItIsAFakeToken"
const name = await search(phone, token);
expect(name).toEqual("Mike");
});
And this is my search function
const searchContact = async (phone, token) => {
const config = {
method: "get",
url: "https://userdatabase/api/search",
token,
params: {
phone
},
};
const response = await axios(config);
return response.name;
}
It returned me "undefined" of response, However, if I change my API call to the below code without using config parameter, I can get the expected data. The thing is I need to pass several args in the real code.
const response = await axios.get("https://userdatabase/api/search");
Please help. Thank you.
I figured it out the reason.
In my unit test file, I use axios.get.mockResolvedValue, it should be axios.mockResolvedValue (remove get). Since I don't use axios.get in the method which are tested.
Related
I have a backend function that returns a request based on an id.
I need it to return me based on an array of ids
If I pass only one ID it works fine, but if I pass more than one, it gives error....
Can someone help?
Thanks
Backend function:
` import prisma from '../../../../libs/prisma'
export default async function handle(req, res) {
const { ids } = req.query
const idsArray = ids.split(',')
const products = await prisma.product.findMany({
where: {
id: {
in: Number(idsArray),
},
},
})
res.json(products)
}`
And Frontend function that passes the parameters on the front is:
const { selectProducts, setSelectProducts } = useContext(ProductContext)
const [productsInfo, setProductsInfo] = useState([])
useEffect(() => {
const uniqIds = [...new Set(selectProducts)]
fetch('/api/products/getAllSelectedProducts?ids=' + uniqIds.join(',')).then((response) => response.json()).then((json) => setProductsInfo(json))
}, [selectProducts])
I am actually a really beginner with this stuff so I beg your pardon for my (silly) questions.
I want to use async functions inside tsx pages, specifically those functions are fetching calls from shopify to get data and ioredis calls to write and read some data.
I know that remix uses action loader functions, so to manage shopify calls I figured out this
export const loader: LoaderFunction = async ({ params }) => {
return json(await GetProductById(params.id as string));
};
async function GetProductById(id: string) {
const ops = ...;
const endpoint = ...;
const response = await fetch(endpoint, ops);
const json = await response.json();
return json;
};
export function FetchGetProductById(id: number) {
const fetcher = useFetcher();
useEffect(() => {
fetcher.load(`/query/getproductid/${id}`);
}, []);
return fetcher.data;
}
with this solution I can get the data whenever I want just calling FetchGetProductById, but my problem is that I need to send more complex data to the loader (like objects)
How may I do that?
In Remix, the loader only handles GET requests, so data must be in the URL, either via params or searchParams (query string).
If you want to pass data in the body of the request, then you'll need to use POST and create an action.
NOTE: Remix uses FormData and not JSON to send data. You will need to convert your JSON into a string.
export const action = async ({ request }: ActionArgs) => {
const formData = await request.formData();
const object = JSON.parse(formData.get("json") as string);
return json(object);
};
export default function Route() {
const fetcher = useFetcher();
useEffect(() => {
if (fetcher.state !== 'idle' || fetcher.data) return;
fetcher.submit(
{
json: JSON.stringify({ a: 1, message: "hello", b: true }),
},
{ method: "post" }
);
}, [fetcher]);
return <pre>{JSON.stringify(fetcher.data, null, 2)}</pre>
}
I need to test the following createFacebookAdVideoFromUrl() that consumes a retryAsyncCall that I'd like to stub with Sinon :
async function createFacebookAdVideoFromUrl(accountId, videoUrl, title, facebookToken = FACEBOOK_TOKEN, options = null, businessId = null) {
const method = 'POST';
const url = `${FACEBOOK_URL}${adsSdk.FacebookAdsApi.VERSION}/${accountId}/advideos`;
const formData = {
access_token: businessId ? getFacebookConfig(businessId).token : facebookToken,
title,
name: title,
file_url: videoUrl,
};
const callback = () => requestPromise({ method, url, formData });
const name = 'createFacebookAdVideoFromUrl';
const retryCallParameters = buildRetryCallParameters(name, options);
const adVideo = await retryAsyncCall(callback, retryCallParameters);
logger.info('ADVIDEO', adVideo);
return { id: JSON.parse(adVideo).id, title };
}
This retryAsyncCall function is exported as such:
module.exports.retryAsyncCall = async (callback, retryCallParameters, noRetryFor = [], customRetryCondition = null) => {
// Implementation details ...
}
Here is how I wrote my test so far:
it.only("should create the video calling business's Facebook ids", async () => {
const payload = createPayloadDataBuilder({
businessId: faker.internet.url(),
});
const retryAsyncCallStub = sinon.stub(retryAsyncCallModule, 'retryAsyncCall').resolves('random');
const createdFacebookAd = await FacebookGateway.createFacebookAdVideoFromUrl(
payload.accountId,
payload.videoUrl,
payload.title,
payload.facebookToken,
payload.options,
payload.businessId,
);
assert.strictEqual(retryAsyncCallStub.calledOnce, true);
assert.strictEqual(createdFacebookAd, { id: 'asdf', title: 'asdf' });
});
I don't expect it to work straightaway as I am working in TDD fashion, but I do expect the retryAsyncCall to be stubbed out. Yet, I am still having this TypeError: Cannot read property 'inc' of undefined error from mocha, which refers to an inner function of retryAsyncCall.
How can I make sinon stubbing work?
I fixed it by changing the way to import in my SUT :
// from
const { retryAsyncCall } = require('../../../helpers/retry-async');
// to
const retry = require('../../../helpers/retry-async');
and in my test file :
// from
import * as retryAsyncCallModule from '../../../src/common/helpers/retry-async';
// to
import retryAsyncCallModule from '../../../src/common/helpers/retry-async';
The use of destructuring seemed to make a copy instead of using the same reference, thus, the stub was not applied on the right reference.
I am new to using Jest for unit tests. How can I mock this simple http request method "getData"? Here is the class:
const got = require("got")
class Checker {
constructor() {
this.url
this.logData = this.logData.bind(this);
this.getData = this.getData.bind(this);
}
async getData(url) {
const response = await got(url);
const data = await response.body;
return data;
}
async logData(first, second, threshold) {
let data = await this.getData(this.url)
console.log("received " + data.body);
}
}
I am trying to mock "getData" so I can write a unit test for "logData". Do I need to mock out the entire "got" module? Thanks.
If you change invoking got to got.get you should be able to have a working test like so:
const got = require('got');
const Checker = require('../index.js');
describe("some test", () => {
beforeEach(() => {
jest.spyOn(got, 'get').mockResolvedValue({ response: { body: { somekey: "somevalue" } } } );
});
it("works", async () => {
new Checker().getData();
expect(got.get).toBeCalledTimes(1);
})
})
One approach is to use dependency injection. Instead of calling 'got' directly, you can 'ask for it' in the class constructor and assign it to a private variable. Then, in the unit test, pass a mock version instead which will return what you want it to.
const got = require("got");
class Checker {
constructor(gotService) {
this.got = gotService;
this.logData = this.logData.bind(this);
this.getData = this.getData.bind(this);
}
async getData(url) {
const response = await this.got(url);
const data = await response.body;
return data;
}
async logData(first, second, threshold) {
let data = await this.getData(this.url)
console.log("received " + data.body);
}
}
//real code
const real = new Checker(got);
//unit testable code
const fakeGot = () => Promise.resolve(mockedData);
const fake = new Checker(fakeGot);
Here is what we are doing:
'Inject' got into the class.
In the class, call our injected version instead of directly calling the original version.
When it's time to unit test, pass a fake version which does what you want it to.
You can include this directly inside your test files. Then trigger the test that makes the Http request and this will be provided as the payload.
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ data: { eth: 0.6, btc: 0.02, ada: 1 } }),
})
);
it('should return correct mock token values', async () => {
const addresses = ["mockA", "mockB", "mockC"];
const res = await getTokenData(addresses);
expect(res.data).toEqual({ eth: 0.6, btc: 0.02, ada: 1 });
});
I have a module that looks like follows:
calculate-average.js
const fetch = require('node-fetch')
const stats = require('stats-lite')
const BASE_URL = 'https://www.example.com/api'
const calculateAverage = async(numApiCalls) => {
const importantData = []
for (let i = 0; i < numApiCalls; i++) {
const url = `${BASE_URL}/${i}` // will make requests to https://www.example.com/api/0, https://www.example.com/api/1 and so on....
const res = await fetch(url)
const jsonRes = await res.json()
importantData.push(jsonRes.importantData)
}
return stats.mean(importantData)
}
module.exports = calculateAverage
I tried testing it along the following lines but I am clearly way off from the solution:
calculate-average.test.js
const calculateAverage = require('../calculate-average')
jest.mock(
'node-fetch',
() => {
return jest.fn(() => {})
}
)
test('Should calculate stats for liquidation open interest delatas', async() => {
const stats = await calculateAverage(100) // Should make 100 API calls.
console.log(stats)
})
What I need to do is the following:
Be able to specify custom varied responses for each API call. For example, I should be able to specify that a call to https://www.example.com/api/0 returns { importantData: 0 }, a call to https://www.example.com/api/1 returns { importantData: 1 } and so on...
If a request is made to a url that I have not specified a response for, a default response is provided. For example if a response is made to https://www.example.com/api/101, then a default response of { importantData: 1000 } is sent.
I would preferably like to do this only using Jest without depending on modules like mock-fetch and jest-mock-fetch. However, if the solution without using is way too complex, then I would be happy to use them. Just don't want to create unnecessary dependencies if not required.
Sure you can! You can use mock function mockResolvedValueOnce method to return a result for a specific call and mockResolvedValue to return the default result.
jest.mock('node-fetch', () => {
const generateResponse = (value) => {
return { json: () => ({ importantData: value }) };
};
return jest
.fn()
.mockResolvedValue(generateResponse(1000)) // default response
.mockResolvedValueOnce(generateResponse(0)) // response for first call
.mockResolvedValueOnce(generateResponse(1)) // response for second call
.mockResolvedValueOnce(generateResponse(2)); // response for third call
});
Note that we are returning an object with the json property so that it returns the json data when you call res.json() in calculate-average.js.
If you want to return a specific response based on the url parameter, you will have to mock the desired behaviour in the returned mock function for node-fetch. The following example will mock the returned value so that for URLs where the counter is greater than 100 it will return 1000. Otherwise, it will return the same value present in the url:
jest.mock('node-fetch', () => {
return jest.fn((url) => {
// Get and parse the URL parameter.
const value = parseInt(url.split('/').slice(-1)[0], 10);
return Promise.resolve({
json: () => ({ importantData: value > 100 ? 1000 : value })
});
});
});