Reputation: 1187
So I have a big nested list of strings. The big nested list will eventually get bigger. But I don't want the unit tests to change when the big nested list gets bigger.
I'm returning custom errors and jazz. But that's irrelevant to this question. So I'm going to abstract this to a list of fruit.
What I want is to be able to add items to the bigger list without disrupting the unit test. In other words, I want to see if the complete/haystack/superset includes the partial/needle/subset. (so no deepEquals
). I've tried a lot of different ways to do this, and I keep coming up short.
mocha spec file
import "chai/register-assert";
import { assert } from "chai";
describe.only("Nested Lists and Objects", () => {
let allFoodList = {
smoothieStuff: ["apples", "bananas", "strawberries", "yogurt", "ice"],
saladStuff: ["apples", "carrots", "spinach", "strawberries"],
saladFruit: ["apples", "strawberries"],
};
let fruitList = {
smoothieStuff: ["apples", "bananas", "strawberries"],
saladStuff: ["apples", "strawberries"],
saladFruit: ["apples", "strawberries"],
};
// None of these work
it("deepNestedInclude", () => {
assert.deepNestedInclude(allFoodList, fruitList);
});
it("deepInclude", () => {
assert.deepInclude(allFoodList, fruitList);
});
it("include", () => {
assert.include(allFoodList, fruitList);
});
it.skip("notInclude", () => {
assert.notInclude(allFoodList, fruitList);
});
it("deepInclude", () => {
assert.deepInclude(allFoodList, fruitList);
});
it.skip("notDeepInclude", () => {
assert.notDeepInclude(allFoodList, fruitList);
});
it("nestedInclude", () => {
assert.nestedInclude(allFoodList, fruitList);
});
it("deepNestedInclude", () => {
assert.deepNestedInclude(allFoodList, fruitList);
});
it("ownInclude", () => {
assert.ownInclude(allFoodList, fruitList);
});
it.skip("notOwnInclude", () => {
assert.notOwnInclude(allFoodList, fruitList);
});
it("deepOwnInclude", () => {
assert.deepOwnInclude(allFoodList, fruitList);
});
it.skip("notDeepOwnInclude", () => {
assert.notDeepOwnInclude(allFoodList, fruitList);
});
it("sameMembers", () => {
assert.sameMembers(allFoodList, fruitList);
});
it("sameDeepMembers", () => {
assert.sameDeepMembers(allFoodList, fruitList);
});
it("includeMembers", () => {
assert.include(allFoodList, fruitList);
});
it("includeDeepMembers", () => {
assert.include(allFoodList, fruitList);
});
});
summary
0 passing (22ms)
4 pending
12 failing
I skipped the 'Not' tests because they would also pass for something that doesn't contain the required items. I also didn't put in anything that said "ordered" because I'm pretty sure that's an extra constraint.
Upvotes: 2
Views: 1325
Reputation: 2400
There are plenty of npm packages that compare plain objects, I wrote one too, ast-compare
.
For context, I came here wanting to double-check, does a native shallow comparison solution exists on Node Assert
, apparently it does not. I'm using uvu
test runner which is very spartan and it ships with only deep equal comparison.
Upvotes: 0
Reputation: 1187
This is not the answer I agree with, but it's the answer I'm going with if I don't get something better.
I appreciate Scott Sauyet's answer, but it falls a little short. I knew I should have given a more detailed example, but I was tired and frustrated. The problem is that the arrays and objects can be rather deeply nested. I ended up going with a recursive function.
I am open to critique and advice on how to make this better/more robust.
Explanation:
"something"[0][0][0][0][0]
does exist. "s"[0]==="s"
.homebrew.assert.js
import { assert } from "chai";
function nestedInclude(haystack, needle, path) {
try {
assert.deepEqual(haystack, needle);
return true;
} catch {}
try {
assert.deepNestedInclude(haystack, needle);
return true;
} catch {}
try {
assert.includeDeepMembers(haystack, needle);
return true;
} catch {}
if (typeof haystack === "object" || typeof needle === "object") {
for (let key in needle) {
let val = needle[key];
if (val === needle) {
throw new Error("We just entered an infinite loop. Great.");
}
let subPath = path + "." + key;
nestedInclude(haystack[key], needle[key], subPath);
}
return true;
} else {
assert.deepEqual(haystack, needle);
}
}
export { nestedInclude };
export default {
nestedInclude,
};
usage
import homebrew from "~/test/homebrew.assertion.js";
describe("...", ()=>{
/* ... */
it("...", ()=>{
homebrew.nestedInclude(allFoodList, fruitList);
});
});
Upvotes: 0
Reputation: 50787
Can't you just use some helper functions?:
const isSubset = (haystack, needles) =>
needles .every (needle => haystack .includes (needle))
const allAreSubsets = (haystackObj, needleObj) =>
Object .keys (needleObj) .every (key => isSubset (haystackObj[key] || [], needleObj[key]))
const allFoodList = {
smoothieStuff: ["apples", "bananas", "strawberries", "yogurt", "ice"],
saladStuff: ["apples", "carrots", "spinach", "strawberries"],
saladFruit: ["apples", "strawberries"],
}
const fruitList = {
smoothieStuff: ["apples", "bananas", "strawberries"],
saladStuff: ["apples", "strawberries"],
saladFruit: ["apples", "strawberries"],
}
const extraFruit = {
smoothieStuff: ["apples", "bananas", "strawberries"],
saladStuff: ["apples", "strawberries"],
saladFruit: ["apples", "strawberries", "kiwis"],
}
console .log (allAreSubsets (allFoodList, fruitList))
console .log (allAreSubsets (allFoodList, extraFruit))
isSubset
just tells if everything in needles
is also in haystack
. allAreSubsets
does the same thing for every property of needlesObj
, comparing it to the same property of haystackObj
(or to an empty array if it's not there.)
Including something like that, you should be able to make simple assertions about the boolean results.
Upvotes: 1