'Determine if two objects have an identical structure with Jest
I'm working on a React application which has multiple themes. Each theme should have the same structure, which can feature deeply-nested properties, but will have different values. It's the structure I'm wanting to add a test case for.
I'm wanting to use Jest to determine if the structure of each of my themes are identical to ensure that no properties have been missed.
All of my theme objects have an identical structure but with different properties. They're set up similar to this:
{
brand: '...',
navigation: {
background: '...',
link: {
background: '...',
},
...
},
...
}
I'm aware of properties like toEqual and toMatchObject, but both of these fail if I directly try to compare two themes with one another:
expect(themeA).toMatchObject(themeB);
This fails because the values of each property are different. For example, themeA has a brand property set to '#ABC' whereas themeB has the same property set to '#DEF'.
I'm also aware that a structure can be defined using Jest like this:
expect(themeA).toEqual({
brand: expect.any(String),
navigation: {
background: expect.any(String),
...
},
...
})
...but this would require updating the test case every time a new property is added or removed, which I feel complicates the purpose of the test.
Is there a simple way to test if two objects have the same structure but different values?
Solution 1:[1]
You could remove all different values from your object and leave just a structure - then you could simply compare two structures using basic jest toEqual:
const obj1 = {
brand: 'brand 1',
navigation: { opacity: 0.2 },
};
const obj2 = {
brand: 'brand 2',
navigation: { opacity: 0.5 },
};
function normalizeObject(obj) {
return JSON.parse(JSON.stringify(obj), (_key, value) => (
typeof value !== 'object' ? '-' : value
));
}
// then you could compare structures of 2 objects:
// expect(normalizeObject(obj1)).toEqual(normalizeObject(obj2));
// or verify if every element from some array have the same structure:
const themeArray = [obj1, obj2, obj1];
themeArray.forEach((theme) => {
// expect(normalizeObject(theme)).toEqual(normalizeObject(themeArray[0]));
});
// according to number of different types in your comparable objects you may want to modify normalizeObject function a little
function normalizeObject(obj) {
return JSON.parse(JSON.stringify(obj), (_key, value) => {
switch (typeof value) {
case 'number': return 0;
case 'string': return '-';
case 'boolean': return true;
// case ...: return ...;
default: return value;
}
});
}
console.log({
original: obj1,
normalized: normalizeObject(obj1),
});
Solution 2:[2]
This is what Typescript will catch everywhere, automatically. You won't really need to write tests for this type of thing.
type ThemeA = {
brand: string,
navigation: {
background: string,
link?: {
background: string,
},
...
},
...
}
myStyle = { ... }
myStyle: ThemeA // this will be highlighted by your IDE
// if it's not a match, even in tests.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | kajkal |
| Solution 2 | Mark Swardstrom |
