I am new to Jest and currently practicing writing tests for my old project. I am stuck on how to write a test for Amazon’s S3Client.
const s3 = new S3Client({
credentials: {
accessKeyId: bucketAccess,
secretAccessKey: bucketSecret,
},
region: bucketRegion,
});
export const uploadProductHandler = async (req, res) => {
const files = req.files;
const images = [];
//generateRandom fnc generate random value that can be use as Id for a data
const productId = generateRandom(10);
// uploading files to aws ...
try {
files.forEach(async (file) => {
const imageName = generateRandom(32);
images.push(imageName);
const params = {
Bucket: bucketName,
Key: imageName,
Body: file.buffer,
ContentType: file.mimetype,
};
const commad = new PutObjectCommand(params);
await s3.send(commad);
});
} catch {
return res.sendStatus(502);
}
const data = req.body;
const date = Date.now();
const urls = await setUrls(images);
try {
const product = new Product({ ...data, date: date, images: images, productId: productId, imagesUrl: { urls: urls, generated: date } });
await product.save();
res.sendStatus(201);
} catch (err) {
res.status(400).send(err.message);
}
};
jest.mock("@aws-sdk/client-s3", () => ({
PutObjectCommand: jest.fn(),
S3Client: jest.fn(() => ({
send: jest.fn(() => "send"),
})),
}));
describe("upload product", () => {
it("should send status of 502 on failling on saving img", async () => {
await uploadProductHandler(mockReq, mockRes);
const setfail = jest.spyOn(S3Client, "send").mockImplementationOnce(() => Promise.reject("not saved"));
expect(generateRandom).toHaveBeenCalledTimes(3);
expect(generateRandom).toHaveBeenLastCalledWith(32);
expect(PutObjectCommand).toHaveBeenCalledTimes(2);
});
});
I did tried using "S3Client.prototype" insted of S3Client in jest.spyOn it gave same error
My code is giving error of "Property send
does not exist in the provided object"
2
Answers
The problem is that the
send
method that’s accessible onS3Client
instances is not on the constructor and it’s not on its prototype either, and sojest.spyOn
throws the error you see. If you need to usejest.spyOn
you must pass it an instance ofS3Client
(its prototype doesn’t count).Your code sample is still not quite complete to run it and test it as is but I had a go at simplifying it for the sake of testing.
Also, I don’t understand why you have two different mock implementations of
send
: one returned in theS3Client
mock above that returns a "send" string, and a different mock viaspyOn
that returns a rejected promise. Could you not usejest.mock
alone, like in the following excerpt which runs fine for me?Simplified source, call it
src.js
:Test script:
__
If you need two different mock versions of
send
(e.g. one that succeeds), then one solution is to split those tests in different files and define the mock once per file as above.Building up on @aayla-secura‘s response, if you want to test both
send
call results (the one that resolves and the one that rejects), what you could do is declare the send mock as a separate constAnother solution found in the Jest documentation (see https://jestjs.io/docs/es6-class-mocks) could be to use
mockImplementation
:That way, you have the possibility to mock different behaviors