Chai.js: Object contains/includes - javascript

Chai has an include method. I want to test to see if an object contains another object. For example:
var origin = {
name: "John",
otherObj: {
title: "Example"
}
}
I want to use Chai to test if this object contains the following (which it does)
var match = {
otherObj: {
title: "Example"
}
}
Doing this does not appear to work:
origin.should.include(match)

Hei, just published chai-subset. Check this out: https://www.npmjs.org/package/chai-subset
This should work for you)
var chai = require('chai');
var chaiSubset = require('chai-subset');
chai.use(chaiSubset);
var obj = {
a: 'b',
c: 'd',
e: {
foo: 'bar',
baz: {
qux: 'quux'
}
}
};
expect(obj).to.containSubset({
e: {
foo: 'bar',
baz: {
qux: 'quux'
}
}
});

The include and contain assertions can be used as either property based language chains or as methods to assert the inclusion of an object in an array or a substring in a string. When used as language chains, they toggle the contain flag for the keys assertion. [emphasis mine]
So if you're invoking include on an object (not an array or a string), then it only serves to toggle the contain flag for the keys assertion. By the looks of your example, testing for deep equality would make more sense, possibly checking for the key first.
origins.should.include.keys("otherObj");
origins.otherObj.should.deep.equal(match.otherObj);
Actually, now I browse the other examples, you would probably be happiest with this :
origins.should.have.deep.property("otherObj", match.otherObj)

In chai 4.2.0, for example you can use deep include
chaijs doc examples:
// Target array deeply (but not strictly) includes `{a: 1}`
expect([{a: 1}]).to.deep.include({a: 1});
expect([{a: 1}]).to.not.include({a: 1});
// Target object deeply (but not strictly) includes `x: {a: 1}`
expect({x: {a: 1}}).to.deep.include({x: {a: 1}});
expect({x: {a: 1}}).to.not.include({x: {a: 1}});

If you know the level of the subobject you can simply use:
expect(origin.otherObj).to.include(match.otherObj);
https://www.chaijs.com/api/bdd/

In Chai 1.5.0 you will find handy method
includeDeepMembers
http://chaijs.com/releases/

Related

Jest function toHaveBeenCalledWith to ignore object order

Im testing with what arguments a function was called but its not passing because the order of properties inside an object, like:
const obj = {
name: 'Bla',
infos: {
info1: '1',
info2: '2'
}
}
expect(function).toHaveBeenCalledWith(obj)
The error says that was called like this: { name: 'bla', infos: {info2: '2', info1: '1'} }
I changed orders but didn't work.
You could follow a similar approach to this SO answer.
Example:
// Assuming some mock setup like this...
const mockFuncton = jest.fn();
const expectedObj = {
name: 'Bla',
infos: {
info1: '1',
info2: '2'
}
}
// Perform operation(s) being tested...
// Expect the function to have been called at least once
expect(mockFuncton).toHaveBeenCalled();
// Get the 1st argument from the mock function call
const functionArg = mockFuncton.mock.calls[0][0];
// Expect that argument matches the expected object
expect(functionArg).toMatchObject(expectedObj);
// Comparison using toEqual() instead which might be a better approach for your usecase
expect(functionArg).toEqual(expectedObj);
Expect.toMatchObject() Docs
Expect.toEqual() Docs
it('does not care on properties ordering', () => {
const a = jest.fn();
a({ a: 1, b: 2, c: {d1: 1, d2: 2} });
expect(a).toHaveBeenCalledWith({c: {d2: 2, d1: 1}, b: 2, a: 1});
});
passes for me with Jest 24.9.0
Under the hood, Jest applies "isEqual" check, not referential check
But we cannot check for functions equality this way. Also partial matching will need custom matcher.

How to use a destructuring assignment inside an object

Is it possible to use a destructuring assignment inside an object?
This works
const test = {a: 'hey', b: 'hello'}
const {a,b} = test;
const destruct = {
a,
b
};
Would like to do this
const test = {a: 'hey', b: 'hello'}
// something like this
const destruct = {
{a,b}: test
};
const destruct = {
{a}: test,
{b}: test
};
If I understand correctly, it seems the spread syntax is a good fit for what you need.
The spread syntax "..." allows you to "spread" the key/value pairs from a source object (ie test) to a target object (ie destruct):
const test = {
a: 'hey',
b: 'hello',
c: 'goodbye'
}
const destruct = {
// {a,b}: test <-- invalid syntax
...test // equivalent using the "spread" syntax
};
console.log(destruct)
Additionally, if you wanted to select a subset of keys from a source object and spread those into a target object then this can be achieved by the following:
const test = {
a: 'hey',
b: 'hello',
c: 'goodbye'
}
/* Spread subset of keys from source object to target object */
const welcomeOnly = {
...({ a, b } = test, { a, b })
}
console.log('exclude goodbye, show welcomes only:', welcomeOnly);
The second example works by destructing the source object (ie test) into an object, with the subset of keys that we want (a and b).
In the scope of that expression (ie everything between the ( and )), these keys are accessible as local variables. We take advantage of this, and pass those to a new object (ie { a, b }). Because the new object is declared after the ,, it is returned as the result of the expression.
If you are trying to take a subset of properties you can use the rest operator
const test = {
a: 'hey',
b: 'hello',
c: 'goodbye'
};
const { c, ...destruct } = test;
console.log(destruct);
This assigns c to a const and the the left over properties are assigned to the const destruct. List all the unwanted properties first and then the left over properties are caught with the rest operator.
Works with arrays as well.
const test = ['hey', 'hello', 'goodbye'];
const [ first, ...rest ] = test;
console.log(rest);
You can try to work like this for destructuring arrays!
let abc = {
a: 'hello',
b: 'hey',
c: 'hi, there!'
}
let {a: x, b:y, c:z} = abc;
console.log(x,y,z)
// "hello"
"hey"
"hi, there!"

Excluding missing keys from JSON comparison

I have two JSON documents that I want to assert equal for Jest unit testing. They should be equal, except the second one has one more key: _id.
Example:
doc1.json
{
username: 'someone',
firstName: 'some',
lastName: 'one',
}
doc2.json
{
_id: '901735013857',
username: 'someone',
firstName: 'some',
lastName: 'one',
}
My code currently looks like this:
const result = await activeDirectoryUserCollection
.findOne({username: testUser1.username});
expect(result).toBe(testUser1);
Obviously this gives the error that they are not equal, just because of that one value.
I'm looking for an alternative to .toBe() that doesn't completely compare the docs, but checks if one is a subset of another. (or something like that).
Alternatively I would appreciate someone to point me to a module that could help me out.
I would checkout Lodash module's .isMatch function.
It performs a partial deep comparison between object and source to determine if object contains equivalent property values.
https://lodash.com/docs/4.17.11#isMatch
Example:
var object = { 'a': 1, 'b': 2 };
_.isMatch(object, { 'b': 2 });
// => true
_.isMatch(object, { 'b': 1 });
// => false
You can iterate through one Object and use the key to assert value in both Objects. Read More for...in
const result = await activeDirectoryUserCollection
.findOne({username: testUser1.username});
for (const prop in testUser1) {
if (testUser1[prop]) {
expect(result[prop]).toBe(testUser1[prop]); // or use .toEqual
}
}
I don't think you need to look outside jest for this. You can use expect.objectContaining(), which is described in the docs as:
expect.objectContaining(object) matches any received object that recursively matches the expected properties. That is, the expected object is a subset of the received object. Therefore, it matches a received object which contains properties that are present in the expected object.
You could use it like:
test('objects', () => {
expect(doc2).toEqual(
expect.objectContaining(doc1)
);
});

Destructuring deep properties

I recently started using ES6's destructuring assignment syntax and started to get familiar with the concept. I was wondering if it's possible to extract a nested property using the same syntax.
For example, let's say I have the following code:
let cagingIt = {
foo: {
bar: 'Nick Cage'
}
};
I know I am able to access extract foo into a variable by doing:
// where foo = { bar: "Nick Cage" }
let { foo } = cagingIt;
However, is it possible to extract a deeply nested property, like bar. Perhaps something like this:
// where bar = "Nick Cage"
let { foo[bar] } = cagingIt;
I've tried finding documentation on the matter but to no avail. Any help would be greatly appreciated. Thank you!
There is a way to handle nested objects and arrays using this syntax. Given the problem described above, a solution would be the following:
let cagingIt = {
foo: {
bar: 'Nick Cage'
}
};
let { foo: {bar: name} } = cagingIt;
console.log(name); // "Nick Cage"
In this example, foo is referring to the property name "foo". Following the colon, we then use bar which refers to the property "bar". Finally, name acts as the variable storing the value.
As for array destructuring, you would handle it like so:
let cagingIt = {
foo: {
bar: 'Nick Cage',
counts: [1, 2, 3]
}
};
let { foo: {counts: [ ct1, ct2, ct3 ]} } = cagingIt;
console.log(ct2); // prints 2
It follows the same concept as the object, just you are able to use array destructuring and store those values as well.
You can destructure a property "as" something else:
const { foo: myFoo } = { foo: 'bar' } // myFoo == 'bar'
Here foo was destructured as myFoo. You can also destructure an object "as" a destructured object
const { foo: { bar } } = { foo: { bar: 'baz' } } // bar == 'baz'
Only one variable was defined in each situation, myFoo and bar, and you can see how they are in similar locations as well, except bar has the { } around it.
You can do this for as many layers of nesting as you like, but if you aren't careful going too many level deep you'll get the old "Cannot read properties of undefined(reading 'foo')".
// Here's an example that doesn't work:
const foo = { bar: { notBaz: 1 } };
const {
bar: {
baz: { // baz is undefined in foo, so by trying to destructure it we're trying to access a property of 'undefined'
qux
}
}
} = foo;
// throws Uncaught TypeError: Cannot read properties of undefined (reading 'baz')
// because baz is 'undefined'
// Won't run due to error above
console.log(qux);
In this case it should be obvious that we shouldn't be trying to destructure it because we can see the definition of foo on the previous line doesn't define the property baz. If the object is coming from an API, though, you aren't always guaranteed that every nested property of your expected result will be non-null or not undefined and you can't know beforehand.
You can set a default value for a destructured object by adding = {}:
const { foo: myFoo = 'bar' } = { baz: 'qux' }; // myFoo == 'bar'
const { bar: { baz } = {} } = { qux: 'quuz' } // baz == undefined
// baz is destructured from the object that was set as the default for foo, which is undefined
// this would throw without the default object, as were trying to destructure from 'undefined'
You can do this for deeply nested destructurings:
// Here's an example that works:
const foo = { bar: { notBaz: 1 } };
const {
bar: {
baz: {
qux // you can default the final item to anything you like as well including null or undefined, but it will be undefined in this case
} = {} // if 'baz' undefined, return {}
} = {} // if 'bar' undefined, return {}
} = foo;
console.log(qux); // logs 'undefined'
If any property is null or undefined along the way, it will cause a cascade of returning empty objects, whose properties to be destructured at the next level will just be undefined. This gets out of hand really quickly though with deeper objects, which can be many lines of code with this formatting. Here's another option that does the same exact thing.
const foo = { bar: { notBaz: 1 } };
const {qux} = foo?.bar?.baz ?? {}; // optional chaining and nullish coalescing
If at any point along the way foo, bar, or baz is null or undefined or null, it will return an empty object that you can destructure( the empty object after ??.
It doesn't make much sense to use destructuring on { qux } if you only need to extract one property, though, because this also requires us to add the nullish coalesced value ?? {}. Below is probably better.
const foo = { bar: { notBaz: 1 } };
const { qux, quux, quuz } = foo?.bar?.baz ?? {}; // this method is better for multiple properties
const quxFromBaz = foo?.bar?.baz?.qux; // this method is better for single properties
For me personally, I think it looks a little messy to include all the optional chaining question marks, but it's better than the alternative with nested destructuring and default values at every level.
And it works with arrays
const foo = {
bar: [
{ a: 1 },
{ b: 2 }
]
}
const c = foo?.bar?.[2]?.c // c == undefined
// bar[2] will be undefined, so trying to access property 'c' would normally throw an error
If you have lodash installed, you can use one of the following:
_.get
var object = { 'a': [{ 'b': { 'c': 3 } }] };
_.get(object, 'a[0].b.c');
// => 3
or if you need multiple keys.
_.at
var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };
_.at(object, ['a[0].b.c', 'a[1]']);
// => [3, 4]
You can also safely pair _.at() up with with Array destructuring. Handy for json responses.
[title, artist, release, artwork] = _.at(object, [
'items[0].recording.title',
'items[0].recording.artists[0].name',
'items[0].recording.releases[0].title',
'items[0].recording.releases[0].artwork[0].url'
]);
Three Levels Deep
In case this helps anyone, here's a bit of code that shows how to destructure three levels deep. In this case, I'm using the find() method on an array.
const id = someId
array.find(({ data: { document: { docId }, }, }) => docId == id)
Above, the array data is structured like this (each obj in the array is the same shape):
[{
isSuccess: true,
isLoading: false,
data: {
foo: bar,
...,
document: {
docId: '123',
...
},
}}]

Combine or merge JSON on node.js without jQuery

I have multiple JSON like those
var object1 = {name: "John"};
var object2 = {location: "San Jose"};
They are not nesting or anything like that. Just basically different fields. I need to combine them into one single JSON in node.js like this:
{name: "John", location: "San Jose"}
I can use jQuery just fine. Here is a working example in the browser:
http://jsfiddle.net/qhoc/agp54/
But if I do this in node.js, I don't want to load jQuery (which is a bit over use, plus node.js' jQuery doesn't work on my Windows machine).
So is there a simple way to do things similar to $.extend() without jQuery?
You should use "Object.assign()"
There's no need to reinvent the wheel for such a simple use case of shallow merging.
The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.
var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };
var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1); // { a: 1, b: 2, c: 3 }, target object itself is changed
console.log(obj === o1) // true
Even the folks from Node.js say so:
_extend was never intended to be used outside of internal NodeJS modules. The community found and used it anyway.
It is deprecated and should not be used in new code. JavaScript comes with very similar built-in functionality through Object.assign.
Update:
You could use the spread operator
Since version 8.6, it's possible to natively use the spread operator in Node.js. Example below:
let o1 = { a: 1 };
let o2 = { b: 2 };
let obj = { ...o1, ...o2 }; // { a: 1, b: 2 }
Object.assign still works, though.
**PS1**: If you are actually interested in **deep merging** (in which internal object data -- in any depth -- is recursively merged), you can use packages like [deepmerge][4], [assign-deep][5] or [lodash.merge][6], which are pretty small and simple to use.
**PS2**: Keep in mind that **Object.assign doesn't work with 0.X versions of Node.js**. If you are working with one of those versions (_you really shouldn't by now_), you could use `require("util")._extend` as shown in the Node.js link above -- for more details, check [tobymackenzie's answer to this same question](https://stackoverflow.com/a/22286375/36272).
If using Node version >= 4, use Object.assign() (see Ricardo Nolde's answer).
If using Node 0.x, there is the built in util._extend:
var extend = require('util')._extend
var o = extend({}, {name: "John"});
extend(o, {location: "San Jose"});
It doesn't do a deep copy and only allows two arguments at a time, but is built in. I saw this mentioned on a question about cloning objects in node: https://stackoverflow.com/a/15040626.
If you're concerned about using a "private" method, you could always proxy it:
// myutil.js
exports.extend = require('util')._extend;
and replace it with your own implementation if it ever disappears. This is (approximately) their implementation:
exports.extend = function(origin, add) {
if (!add || (typeof add !== 'object' && add !== null)){
return origin;
}
var keys = Object.keys(add);
var i = keys.length;
while(i--){
origin[keys[i]] = add[keys[i]];
}
return origin;
};
Underscore's extend is the easiest and quickest way to achieve this, like James commented.
Here's an example using underscore:
var _ = require('underscore'), // npm install underscore to install
object1 = {name: "John"},
object2 = {location: "San Jose"};
var target = _.extend(object1, object2);
object 1 will get the properties of object2 and be returned and assigned to target.
You could do it like this as well, depending on whether you mind object1 being modified:
var target = {};
_.extend(target, object1, object2);
A normal loop?
function extend(target) {
var sources = [].slice.call(arguments, 1);
sources.forEach(function (source) {
for (var prop in source) {
target[prop] = source[prop];
}
});
return target;
}
var object3 = extend({}, object1, object2);
That's a basic starting point. You may want to add things like a hasOwnProperty check, or add some logic to handle the case where multiple source objects have a property with the same identifier.
Here's a working example.
Side note: what you are referring to as "JSON" are actually normal JavaScript objects. JSON is simply a text format that shares some syntax with JavaScript.
Use merge.
$ npm install merge
Sample code:
var merge = require('merge'), // npm install -g merge
original, cloned;
console.log(
merge({ one: 'hello' }, { two: 'world' })
); // {"one": "hello", "two": "world"}
original = { x: { y: 1 } };
cloned = merge(true, original);
cloned.x.y++;
console.log(original.x.y, cloned.x.y); // 1, 2
I see that this thread is too old, but I put my answer here just in logging purposes.
In one of the comments above you mentioned that you wanted to use
'express' in your project which has 'connect' library in the
dependency list. Actually 'connect.utils' library contains a 'merge'
method that does the trick. So you can use the 3rd party
implementation without adding any new 3rd party libraries.
Here is simple solution, to merge JSON.
I did the following.
Convert each of the JSON to strings using JSON.stringify(object).
Concatenate all the JSON strings using + operator.
Replace the pattern /}{/g with ","
Parse the result string back to JSON object
var object1 = {name: "John"};
var object2 = {location: "San Jose"};
var merged_object = JSON.parse((JSON.stringify(object1) + JSON.stringify(object2)).replace(/}{/g,","))
The resulting merged JSON will be
{name: "John", location: "San Jose"}
There is an easy way of doing it in Node.js
var object1 = {name: "John"};
var object2 = {location: "San Jose"};
To combine/extend this we can use ... operator in ECMA6
var object1 = {name: "John"};
var object2 = {location: "San Jose"};
var result = {
...object1,
...object2
}
console.log(result)
You can also use this lightweight npm package called absorb
It is 27 lines of code, 1kb and uses recursion to perform deep object merges.
var absorb = require('absorb');
var obj1, obj2;
obj1 = { foo: 123, bar: 456 };
obj2 = { bar: 123, key: 'value' }
absorb(obj1, obj2);
console.log(obj1); // Output: { foo: 123, bar: 123, key: 'value' }
You can also use it to make a clone or only transfer values if they don't exist in the source object, how to do this is detailed in the link provided.
It can easy be done using Object.assign() method -
var object1 = {name: "John"};
var object2 = {location: "San Jose"};
var object3 = Object.assign(object1,object2);
console.log(object3);
now object3 is { name: 'John', location: 'San Jose' }
Use spread operator. It is supported in Node since version 8.6
const object1 = {name: "John"};
const object2 = {location: "San Jose"};
const obj = {...object1, ...object2}
console.log(obj)
// {
// "name": "John",
// "location": "San Jose"
// }
If you need special behaviors like nested object extension or array replacement you can use Node.js's extendify.
var extendify = require('extendify');
_.extend = extendify({
inPlace: false,
arrays : 'replace',
isDeep: true
});
obj1 = {
a:{
arr: [1,2]
},
b: 4
};
obj2 = {
a:{
arr: [3]
}
};
res = _.extend(obj1,obj2);
console.log(JSON.stringify(res)); //{'a':{'arr':[3]},'b':4}
Lodash is a another powerful tool-belt option for these sorts of utilities. See: _.merge() (which is recursive)
var object = {
'a': [{ 'b': 2 }, { 'd': 4 }]
};
var other = {
'a': [{ 'c': 3 }, { 'e': 5 }]
};
_.merge(object, other);
// => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }
The below code will help you to merge two JSON object which has nested objects.
function mergeJSON(source1,source2){
/*
* Properties from the Souce1 object will be copied to Source2 Object.
* Note: This method will return a new merged object, Source1 and Source2 original values will not be replaced.
* */
var mergedJSON = Object.create(source2);// Copying Source2 to a new Object
for (var attrname in source1) {
if(mergedJSON.hasOwnProperty(attrname)) {
if ( source1[attrname]!=null && source1[attrname].constructor==Object ) {
/*
* Recursive call if the property is an object,
* Iterate the object and set all properties of the inner object.
*/
mergedJSON[attrname] = zrd3.utils.mergeJSON(source1[attrname], mergedJSON[attrname]);
}
} else {//else copy the property from source1
mergedJSON[attrname] = source1[attrname];
}
}
return mergedJSON;
}
You can use Lodash
const _ = require('lodash');
let firstObject = {'email' : 'email#email.com};
let secondObject = { 'name' : { 'first':message.firstName } };
_.merge(firstObject, secondObject)
A better approach from the correct solution here in order to not alter target:
function extend(){
let sources = [].slice.call(arguments, 0), result = {};
sources.forEach(function (source) {
for (let prop in source) {
result[prop] = source[prop];
}
});
return result;
}
You can do it inline, without changing any variables like this:
let obj1 = { name: 'John' };
let obj2 = { surname: 'Smith' };
let obj = Object.assign({}, obj1, obj2); // { name: 'John', surname: 'Smith' }
Let object1 and object2 be two JSON object.
var object1 = [{"name": "John"}];
var object2 = [{"location": "San Jose"}];
object1.push(object2);
This will simply append object2 in object1:
[{"name":"John"},{"location":"San Jose"}]

Categories