Jest's expect.objectContaining() fails in expect.toHaveBennCalled() - javascript

I'm trying to write a test to vlaidate code that writes to DynamoDB with aws-sdk. Despite a very similar use case being presented in the offical docs (https://jestjs.io/docs/en/expect#expectobjectcontainingobject), my assertion fails. Any help appreciated.
This is my test:
test("givenCprRepositoryServiceTestSuite_whenSaveCprRecord_thenMetaExpiresAtAppended", async () => {
await cprRepositoryService.saveCprRecord({cprNumber: existingCpr, firstName: "Jens", lastName: "Jensen"})
expect(aws.DynamoDB.DocumentClient.prototype.put).toHaveBeenCalledWith(
expect.objectContaining({
Item: {
metaExpiresAt: expect.any(Number)
}
}))
})
And the error returned is:
Error:
expect(jest.fn()).toHaveBeenCalledWith(...expected)
Expected ObjectContaining:
{"Item": {"metaExpiresAt": Any<Number>}}
Received:
{"Item": {"cprNumber": "1234567890", "firstName": "Jens", "lastName": "Jensen", "metaExpiresAt": 1570792526}, "TableName": "CountryLayer_Cpr"}

The expect.objectContaining(object) method cannot handle nested objects. To resolve this problem, I'd recommend the following modifications to your code.
test("givenCprRepositoryServiceTestSuite_whenSaveCprRecord_thenMetaExpiresAtAppended", async () => {
await cprRepositoryService.saveCprRecord({cprNumber: existingCpr, firstName: "Jens", lastName: "Jensen"})
expect(aws.DynamoDB.DocumentClient.prototype.put).toHaveBeenCalledWith(
expect.objectContaining({
Item: expect.objectContaining({
metaExpiresAt: expect.any(Number)
})
}))
})

Related

How to run a test multiple times with different sets of data in cypress?

While automating a website, I have a requirement to run a test case(it block) multiple times with different set of testdata in cypress.
Please consider the below example :
it('example test', () => {
//first run
getOnDefaultForm.typeUserName('Name1');
getOnDefaultForm.typePassword('Pass1');
getOnDefaultForm.clickSubmit();
//second run
getOnDefaultForm.typeUserName('Name2');
getOnDefaultForm.typePassword('Pass2');
getOnDefaultForm.clickSubmit();
//third run
getOnDefaultForm.typeUserName('Name3');
getOnDefaultForm.typePassword('Pass3');
getOnDefaultForm.clickSubmit();
});
How can I achieve this in Cypress?
I think you need to have a look as this repo: https://github.com/cypress-io/cypress-example-recipes/tree/master/examples/fundamentals__dynamic-tests Or just search this site, this is not the first time someone has asked this very question.
In general, you can wrap your it in a loop. In practice, it'd look e.g. like this:
const testData = [
{
name: 'Name1',
password: 'Pass1'
},
{
name: 'Name2',
password: 'Pass2'
},
{
name: 'Name3',
password: 'Pass3'
}
]
testData.forEach((credentials) => {
it('example test', () => {
getOnDefaultForm.typeUserName(credentials.name);
getOnDefaultForm.typePassword(credentials.password);
getOnDefaultForm.clickSubmit();
})
});
It is also possible to put the data in a json file in the fixtures folder, and import it at the top of the spec file.
This methodology is still working in Cypress version 12.5.0.
fixture
[
{ "name": 'Name1', "password": 'Pass1' },
{ "name": 'Name2', "password": 'Pass2' },
{ "name": 'Name3', "password": 'Pass3' }
]
test
const testData = require('../fixtures/test-data.json')
testData.forEach((credentials) => {
it('example test for ' + credentials.name, () => {
getOnDefaultForm.typeUserName(credentials.name);
getOnDefaultForm.typePassword(credentials.password);
getOnDefaultForm.clickSubmit();
})
})

Why is mongo saying this is not a valid JavaScript object when I try to use the update method?

I am getting this error when I try to update a record:
(node:2018) UnhandledPromiseRejectionWarning: MongoError: document must be a valid JavaScript object
Heres my code that I'm trying to run:
global.db.collection("groups").find({userId: user_id.toString(), groupNumber:parseInt(realGroupNumber)}).toArray( function (err, group) {
if(err) {console.log(err)};
if(group[0]) {
group[0].students.push(NewRow[row]);
//NOTE: HERE IS WHERE THE ERROR HAPPENS (BELOW)
global.db.collection("groups").update({userId: user_id.toString(), groupNumber:parseInt(realGroupNumber)}), group[0], function (err, group) {
if(err) {console.log(err)};
};
};
});
It keeps saying that group[0] is not a valid JavaScript object, but typeof group[0] returns 'object'.
Also here is an example of group[0] console logged out:
{ _id: 5df7fcc562f67c0a0b66aefd,
userId: '5d3f42db404e9e0a91fdd9d8',
groupNumber: 3,
selectedLeaders: [ '5df7d27e2e5e1904552a30a4' ],
roomNumber: '333',
groupNotes: '',
students:
[ { firstName: 'June',
userId: '5d3f42db404e9e0a91fdd9d8',
lastName: 'Lopez',
gender: 'female',
groupId: 3,
roomNumber: '1234',
_id: 5dfac3a34c64110809726571 } ] }
Any help greatly appreciated! Thanks!
It looks like the error is being caused by the closing parenthesis on line 7 below. This is closing the update, which excludes group[0] and the callback from being included:
global.db.collection("groups").update({
userId: user_id.toString(),
groupNumber: parseInt(realGroupNumber)
}), group[0], // <---- HERE
function(err, group) {
if (err) {
console.log(err)
};
};
Try moving it to the end of the block, after the callback:
//NOTE: HERE IS WHERE THE ERROR HAPPENS (BELOW)
global.db.collection("groups").update({
userId: user_id.toString(),
groupNumber: parseInt(realGroupNumber)
}, group[0],
function(err, group) {
if (err) {
console.log(err)
};
});
That said, note that collection.update() has been deprecated for some time. You should look into updating to at least a 3.X version of the Node driver and using updateOne or updateMany:
https://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#update

Use Jest to test an API endpoint and its response

I want to use jest to test an API endpoint to check if it returns a response and if the JSON contains the parameter keys that I need.
My function looks like the following:
export function getFiveDayWeatherByCoordinates(id) {
let url = FORECAST_ID_URL(id);
return fetch(url)
.then(response => response.json())
.then(data => {
return data;
})
.catch(err => console.log(err));
}
It returns a JSON with a set of parameters, I will post only a snapshot:
{
cnt: 14,
cod: "200",
city: {
coord: {lat: 38.7169, lon: -9.1333},
country: "PT",
id: 8012502,
name: "Socorro",
population: 0,
timezone: 3600,
}
Every tutorial that I see until now says to mock the response, but I want to test the actual API.
I would suggest using Frisby.js to test the API responses. It's a great test framework for API testing that runs in Jest. I've used it numerous times to write API and backend integration tests. Although, I typically keep these test suites separate from my UI unit tests.
Here's an example:
it('should return weather coords', async () => {
return frisby
.get(`${global.apiUrl}/my-weather-endpoint`)
.expect('status', 200)
.expect('jsonTypes', Joi.object({
cnt: Joi.number().required(),
cod: Joi.string().required(),
city: Joi.object({
coord: Joi.object({
lat: Joi.number().required(),
lon: Joi.number().required()
}),
country: Joi.string().required(),
id: Joi.number().required(),
name: Joi.string().required(),
population: Joi.number().required(),
timezone: Joi.number().required()
}).required()
});
});
Frisby also encourages the use of the Joi validation framework (it's already included in the npm package).

Why am I getting a validation error even though I'm providing data?

For some reason, the backend of my app doesn't think I'm providing data, even though I am, while testing my routes.
I've looked around, but I haven't found anything that quite matches up to the problem I'm having.
I've been trying to build the backend for my app by following this tutorial series: https://www.youtube.com/playlist?list=PL55RiY5tL51q4D-B63KBnygU6opNPFk_q
Before I added validation to my models, I received 200s and 201s for my HTTP requests. Once I added validation, I began receiving error messages informing me that I'm not providing the necessary information, even though I am.
I'm using Mongoose 5.5.8, Cors 2.8.5, Body-Parser 1.19.0, and Postman 7.1.1.
I've tried using the .exec() method once, but that only gave me a worse error.
Here is my model:
const studentSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
first_name: { type: String, required: true },
last_name: { type: String, required: true },
preferred_name: { type: String, required: true }
});
module.exports = mongoose.model("Student", studentSchema);
Here is my controller:
add_student: (req, res) => {
const student = new Student({
_id: new mongoose.Types.ObjectId(),
first_name: req.body.first_name,
last_name: req.body.last_name,
preferred_name: req.body.preferred_name
});
student
.save()
.then(result => {
console.log(result);
res.status(201).json({
message: "Student added!",
createdStudent: {
first_name: result.first_name,
last_name: result.last_name,
preferred_name: result.preferred_name
}
});
})
.catch(err => {
console.log(err);
res.status(422).json({
error: err
});
});
},
Here is my route:
router.route("/")
.post(studentController.add_student)
And here is the data I'm trying to pass through Postman:
{
"first_name": "Steve",
"last_name": "Jones",
"preferred_name": "Stevie Boy"
}
As you can see, I am passing data, but I keep getting errors as if there's no information there at all.
I guess I need to know how to properly provide pass data through my controller, so my app doesn't think I'm trying to pass an empty object.
The problem I was having was a user (me) error. In Postman,
Where it says JSON (application/json), I had it said to text. Setting it to JSON fixed my problem.
And have no doubts, I feel plenty foolish about this!

Nesting data in an express app with postgres

So I'm new to express and have been playing around with a program where you can create events and invite other users. I know that you can use join tables to get the data, but I was hoping that there is a way to have the data nest itself using POSTGRESQL to show the events that the user is hosting, attending, and invited to based on the id of an event.
Here is what I have so far:
app.get('/api/users', function(req, res) {
var list = [];
User.findAll().then(function(users) {
for( var i in users) {
var item = {
id: users[i].dataValues['id'],
firstName: users[i].dataValues['firstname'],
lastName: users[i].dataValues['lastname'],
email: users[i].dataValues['email'],
phone: users[i].dataValues['phone'],
image: users[i].dataValues['image'],
confirmed: users[i].dataValues['confirmed'],
events: []
}
Event.findAll({ where: { hosting_id: user.id }}).then(function(events) {
for( var i in events) {
item.events.push({
hosting: event[i].dataValues['id'],
attending: event[i].dataValues['id'],
invites: event[i].dataValues['id']
})
}
})
list.push(item)
}
res.json(list)
})
});
I've been able to have it so that it loops and can find all the events that are associated with the user through id, but I'm still struggling with how to have it connect with each one so that it can display the event id, like the following example:
{
"id": "4",
"firstName": "Jon",
"lastName": "Doe",
"email": "jdoe#test.com",
"phone": "1234567890",
"image": "",
"events": {
"hosting": [
"1"],
"attending": [],
"invites": [
"3", "4"]
},
"confirmed": true
}
Besides having the info displayed, I'm also trying to figure out if it will need to be nested within a POSTGRES database, or if I can just use something like a join table but still have the info display without having another API call.
Any help would be greatly appreciated! Thank you in advance!
Event.findAll is a Promise and run asynchronously.
You need to make sure that you format your response only after that call.
You can try something like this and see if it works:
Promise.all(
users.map(function(user) {
return Event.findaAll({ where: { hosting_id: user.id }})
.then(function(events) {
var formatEvents = events.map(function(event) {
return {
hosting: event.id,
attending: event.id,
invites: event.id
};
})
return Promise.resolve({
id: users[i].dataValues['id'],
firstName: users[i].dataValues['firstname'],
lastName: users[i].dataValues['lastname'],
email: users[i].dataValues['email'],
phone: users[i].dataValues['phone'],
image: users[i].dataValues['image'],
confirmed: users[i].dataValues['confirmed'],
events: formatEvents
});
})
})
)
.then(function(list) {
res.json(list)
})
Or you can also add a relationship between Users and Events and when get users include the events model.

Categories