assert
@jsenv/assert is the NPM package used by jsenv to write tests.
It can be resumed by the following quote:
equal() is my favorite assertion. If the only available assertion in every test suite was equal(), almost every test suite in the world would be better for it.
— Eric Elliot in Rethinking Unit Test Assertion
Example
import { assert } from "@jsenv/assert";
const actual = { foo: false };
const expected = { foo: true };
assert({ actual, expected });
> node ./docs/demo.mjs
AssertionError: unequal values
--- found ---
false
--- expected ---
true
--- path ---
actual.foo
How it works
assert does nothing when comparison is successfull but throws an error when comparison is failing. To illustrates when a comparison fails, check the list of examples below
Type failure:
import { assert } from "@jsenv/assert";
const actual = 10;
const expected = "10";
try {
assert({ actual, expected });
} catch (e) {
console.log(e.message);
}
AssertionError: unequal values
--- found ---
10
--- expected ---
"10"
--- path ---
actual
Prototype failure:
import { assert } from "@jsenv/assert";
const actual = new TypeError();
const expected = new Error();
try {
assert({ actual, expected });
} catch (e) {
console.log(e.message);
}
AssertionError: unequal prototypes
--- prototype found ---
window.TypeError.prototype
--- prototype expected ---
window.Error.prototype
--- path ---
actual[[Prototype]]
Usage in Node.js
npm i --save-dev @jsenv/assert
import { assert } from "@jsenv/assert";
assert({
actual: true,
expected: false,
});
Usage in a browser via CDN
<script type="module">
import { assert } from "https://unpkg.com/@jsenv/assert@latest/src/main.js";
assert({
actual: true,
expected: false,
});
</script>
Usage in a browser via NPM
npm i --save-dev @jsenv/assert
<script type="module">
import { assert } from "@jsenv/assert";
assert({
actual: true,
expected: false,
});
</script>
Writing tests with @jsenv/assert
This part contain examples where @jsenv/assert is used to write tests.
Exception
import { assert } from "@jsenv/assert";
const getCircleArea = (circleRadius) => {
if (isNaN(circleRadius)) {
throw new TypeError(
`circleRadius must be a number, received ${circleRadius}`,
);
}
return circleRadius * circleRadius * Math.PI;
};
try {
getCircleArea("toto");
throw new Error("should throw"); // this line throw if getCircleArea does not throw as it should
} catch (error) {
const actual = error;
const expected = new TypeError(
`circleRadius must be a number, received toto`,
);
assert({ actual, expected });
}
Async exception
import { assert } from "@jsenv/assert";
const getCircleArea = async (circleRadius) => {
if (isNaN(circleRadius)) {
throw new TypeError(
`circleRadius must be a number, received ${circleRadius}`,
);
}
return circleRadius * circleRadius * Math.PI;
};
try {
await getCircleArea("toto");
throw new Error("should throw"); // this line throw if getCircleArea does not throw as it should
} catch (error) {
const actual = error;
const expected = new TypeError(
`circleRadius must be a number, received toto`,
);
assert({ actual, expected });
}
Note how code testing
getCircleArea
is similar in Assert exception and Assert async exception.
Callback
import { assert } from "@jsenv/assert";
const createAbortSignal = () => {
const abortSignal = {
onabort: () => {},
abort: () => {
abortSignal.onabort();
},
};
return abortSignal;
};
// arrange
const abortSignal = createAbortSignal();
let called = false;
abortSignal.onabort = () => {
called = true;
};
// act
abortSignal.abort();
// assert
const actual = called;
const expected = true;
assert({ actual, expected });
Code above is a great example of the AAA pattern.
Callback with delay
import { assert } from "@jsenv/assert";
const callAfter50Ms = (callback) => {
setTimeout(callback, 50);
};
let called = false;
callAfter50Ms(() => {
called = true;
});
await new Promise((resolve) => setTimeout(resolve, 80));
const actual = called;
const expected = true;
assert({ actual, expected });
Any
import { assert } from "@jsenv/assert";
const createUser = () => {
return {
name: "sam",
creationTime: Date.now(),
};
};
const user = createUser();
const actual = user;
const expected = {
name: "sam",
creationTime: assert.any(Number),
};
assert({ actual, expected });
Not
import { assert } from "@jsenv/assert";
const getRandomDifferentUserName = (user) => {
const getRandomName = () => {
return Array.from({ length: 4 })
.map(() => getRandomLetter())
.join("");
};
const getRandomLetter = () => {
return ALPHABET.charAt(Math.floor(Math.random() * ALPHABET.length));
};
const ALPHABET = "abcdefghijklmnopqrstuvwxyz";
const randomName = getRandomName();
if (randomName === user.name) {
return getRandomDifferentUserName(user);
}
return randomName;
};
const name = getRandomDifferentUserName({ name: "toto" });
const actual = name;
const expected = assert.not("toto");
assert({ actual, expected });
Subset of properties
import { assert } from "@jsenv/assert";
const getUser = () => {
return {
name: "sam",
age: 32,
friends: [], // poor sam :(
};
};
const user = getUser();
const actual = { name: user.name, age: user.age };
const expected = { name: "sam", age: 32 };
assert({ actual, expected });