mirror of
https://github.com/denoland/deno.git
synced 2025-09-27 12:49:10 +00:00
feat(std/testing): Add support for object assertion against object subset (#8001)
This commit add supports for a new assertion function "assertObjectMatch" which allows to test an actual object against an expected object subset (i.e. inclusivity, not equality).
This commit is contained in:
parent
322913ee5c
commit
23df1c563e
3 changed files with 219 additions and 0 deletions
|
@ -25,6 +25,8 @@ pretty-printed diff of failing assertion.
|
||||||
`expected`.
|
`expected`.
|
||||||
- `assertArrayContains()` - Make an assertion that `actual` array contains the
|
- `assertArrayContains()` - Make an assertion that `actual` array contains the
|
||||||
`expected` values.
|
`expected` values.
|
||||||
|
- `assertObjectMatch()` - Make an assertion that `actual` object match
|
||||||
|
`expected` subset object
|
||||||
- `assertThrows()` - Expects the passed `fn` to throw. If `fn` does not throw,
|
- `assertThrows()` - Expects the passed `fn` to throw. If `fn` does not throw,
|
||||||
this function does. Also compares any errors thrown to an optional expected
|
this function does. Also compares any errors thrown to an optional expected
|
||||||
`Error` class and checks that the error `.message` includes an optional
|
`Error` class and checks that the error `.message` includes an optional
|
||||||
|
|
|
@ -429,6 +429,48 @@ export function assertNotMatch(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make an assertion that `actual` object is a subset of `expected` object, deeply.
|
||||||
|
* If not, then throw.
|
||||||
|
*/
|
||||||
|
export function assertObjectMatch(
|
||||||
|
actual: Record<PropertyKey, unknown>,
|
||||||
|
expected: Record<PropertyKey, unknown>,
|
||||||
|
): void {
|
||||||
|
type loose = Record<PropertyKey, unknown>;
|
||||||
|
const seen = new WeakMap();
|
||||||
|
return assertEquals(
|
||||||
|
(function filter(a: loose, b: loose): loose {
|
||||||
|
// Prevent infinite loop with circular references with same filter
|
||||||
|
if ((seen.has(a)) && (seen.get(a) === b)) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
seen.set(a, b);
|
||||||
|
// Filter keys and symbols which are present in both actual and expected
|
||||||
|
const filtered = {} as loose;
|
||||||
|
const entries = [
|
||||||
|
...Object.getOwnPropertyNames(a),
|
||||||
|
...Object.getOwnPropertySymbols(a),
|
||||||
|
]
|
||||||
|
.filter((key) => key in b)
|
||||||
|
.map((key) => [key, a[key as string]]) as Array<[string, unknown]>;
|
||||||
|
// Build filtered object and filter recursively on nested objects references
|
||||||
|
for (const [key, value] of entries) {
|
||||||
|
if (typeof value === "object") {
|
||||||
|
const subset = (b as loose)[key];
|
||||||
|
if ((typeof subset === "object") && (subset)) {
|
||||||
|
filtered[key] = filter(value as loose, subset as loose);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filtered[key] = value;
|
||||||
|
}
|
||||||
|
return filtered;
|
||||||
|
})(actual, expected),
|
||||||
|
expected,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forcefully throws a failed assertion
|
* Forcefully throws a failed assertion
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
assertNotEquals,
|
assertNotEquals,
|
||||||
assertNotMatch,
|
assertNotMatch,
|
||||||
assertNotStrictEquals,
|
assertNotStrictEquals,
|
||||||
|
assertObjectMatch,
|
||||||
assertStrictEquals,
|
assertStrictEquals,
|
||||||
assertStringContains,
|
assertStringContains,
|
||||||
assertThrows,
|
assertThrows,
|
||||||
|
@ -259,6 +260,180 @@ Deno.test("testingAssertStringNotMatchingThrows", function (): void {
|
||||||
assert(didThrow);
|
assert(didThrow);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Deno.test("testingAssertObjectMatching", function (): void {
|
||||||
|
const sym = Symbol("foo");
|
||||||
|
const a = { foo: true, bar: false };
|
||||||
|
const b = { ...a, baz: a };
|
||||||
|
const c = { ...b, qux: b };
|
||||||
|
const d = { corge: c, grault: c };
|
||||||
|
const e = { foo: true } as { [key: string]: unknown };
|
||||||
|
e.bar = e;
|
||||||
|
const f = { [sym]: true, bar: false };
|
||||||
|
// Simple subset
|
||||||
|
assertObjectMatch(a, {
|
||||||
|
foo: true,
|
||||||
|
});
|
||||||
|
// Subset with another subset
|
||||||
|
assertObjectMatch(b, {
|
||||||
|
foo: true,
|
||||||
|
baz: { bar: false },
|
||||||
|
});
|
||||||
|
// Subset with multiple subsets
|
||||||
|
assertObjectMatch(c, {
|
||||||
|
foo: true,
|
||||||
|
baz: { bar: false },
|
||||||
|
qux: {
|
||||||
|
baz: { foo: true },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// Subset with same object reference as subset
|
||||||
|
assertObjectMatch(d, {
|
||||||
|
corge: {
|
||||||
|
foo: true,
|
||||||
|
qux: { bar: false },
|
||||||
|
},
|
||||||
|
grault: {
|
||||||
|
bar: false,
|
||||||
|
qux: { foo: true },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// Subset with circular reference
|
||||||
|
assertObjectMatch(e, {
|
||||||
|
foo: true,
|
||||||
|
bar: {
|
||||||
|
bar: {
|
||||||
|
bar: {
|
||||||
|
foo: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// Subset with same symbol
|
||||||
|
assertObjectMatch(f, {
|
||||||
|
[sym]: true,
|
||||||
|
});
|
||||||
|
// Missing key
|
||||||
|
{
|
||||||
|
let didThrow;
|
||||||
|
try {
|
||||||
|
assertObjectMatch({
|
||||||
|
foo: true,
|
||||||
|
}, {
|
||||||
|
foo: true,
|
||||||
|
bar: false,
|
||||||
|
});
|
||||||
|
didThrow = false;
|
||||||
|
} catch (e) {
|
||||||
|
assert(e instanceof AssertionError);
|
||||||
|
didThrow = true;
|
||||||
|
}
|
||||||
|
assertEquals(didThrow, true);
|
||||||
|
}
|
||||||
|
// Simple subset
|
||||||
|
{
|
||||||
|
let didThrow;
|
||||||
|
try {
|
||||||
|
assertObjectMatch(a, {
|
||||||
|
foo: false,
|
||||||
|
});
|
||||||
|
didThrow = false;
|
||||||
|
} catch (e) {
|
||||||
|
assert(e instanceof AssertionError);
|
||||||
|
didThrow = true;
|
||||||
|
}
|
||||||
|
assertEquals(didThrow, true);
|
||||||
|
}
|
||||||
|
// Subset with another subset
|
||||||
|
{
|
||||||
|
let didThrow;
|
||||||
|
try {
|
||||||
|
assertObjectMatch(b, {
|
||||||
|
foo: true,
|
||||||
|
baz: { bar: true },
|
||||||
|
});
|
||||||
|
didThrow = false;
|
||||||
|
} catch (e) {
|
||||||
|
assert(e instanceof AssertionError);
|
||||||
|
didThrow = true;
|
||||||
|
}
|
||||||
|
assertEquals(didThrow, true);
|
||||||
|
}
|
||||||
|
// Subset with multiple subsets
|
||||||
|
{
|
||||||
|
let didThrow;
|
||||||
|
try {
|
||||||
|
assertObjectMatch(c, {
|
||||||
|
foo: true,
|
||||||
|
baz: { bar: false },
|
||||||
|
qux: {
|
||||||
|
baz: { foo: false },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
didThrow = false;
|
||||||
|
} catch (e) {
|
||||||
|
assert(e instanceof AssertionError);
|
||||||
|
didThrow = true;
|
||||||
|
}
|
||||||
|
assertEquals(didThrow, true);
|
||||||
|
}
|
||||||
|
// Subset with same object reference as subset
|
||||||
|
{
|
||||||
|
let didThrow;
|
||||||
|
try {
|
||||||
|
assertObjectMatch(d, {
|
||||||
|
corge: {
|
||||||
|
foo: true,
|
||||||
|
qux: { bar: true },
|
||||||
|
},
|
||||||
|
grault: {
|
||||||
|
bar: false,
|
||||||
|
qux: { foo: false },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
didThrow = false;
|
||||||
|
} catch (e) {
|
||||||
|
assert(e instanceof AssertionError);
|
||||||
|
didThrow = true;
|
||||||
|
}
|
||||||
|
assertEquals(didThrow, true);
|
||||||
|
}
|
||||||
|
// Subset with circular reference
|
||||||
|
{
|
||||||
|
let didThrow;
|
||||||
|
try {
|
||||||
|
assertObjectMatch(e, {
|
||||||
|
foo: true,
|
||||||
|
bar: {
|
||||||
|
bar: {
|
||||||
|
bar: {
|
||||||
|
foo: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
didThrow = false;
|
||||||
|
} catch (e) {
|
||||||
|
assert(e instanceof AssertionError);
|
||||||
|
didThrow = true;
|
||||||
|
}
|
||||||
|
assertEquals(didThrow, true);
|
||||||
|
}
|
||||||
|
// Subset with symbol key but with string key subset
|
||||||
|
{
|
||||||
|
let didThrow;
|
||||||
|
try {
|
||||||
|
assertObjectMatch(f, {
|
||||||
|
foo: true,
|
||||||
|
});
|
||||||
|
didThrow = false;
|
||||||
|
} catch (e) {
|
||||||
|
assert(e instanceof AssertionError);
|
||||||
|
didThrow = true;
|
||||||
|
}
|
||||||
|
assertEquals(didThrow, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Deno.test("testingAssertsUnimplemented", function (): void {
|
Deno.test("testingAssertsUnimplemented", function (): void {
|
||||||
let didThrow = false;
|
let didThrow = false;
|
||||||
try {
|
try {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue