Skip to content

Commit

Permalink
Refactor asserts in testing (#227)
Browse files Browse the repository at this point in the history
  • Loading branch information
zekth authored and ry committed Mar 5, 2019
1 parent 8fb9f70 commit c734e32
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 140 deletions.
179 changes: 179 additions & 0 deletions testing/asserts.ts
@@ -0,0 +1,179 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { assertEqual as prettyAssertEqual } from "./pretty.ts";

interface Constructor {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
new (...args: any[]): any;
}

/** Make an assertion, if not `true`, then throw. */
export function assert(expr: boolean, msg = ""): void {
if (!expr) {
throw new Error(msg);
}
}

/**
* Make an assertion that `actual` and `expected` are equal, deeply. If not
* deeply equal, then throw.
*/
export function equal(actual: unknown, expected: unknown, msg?: string): void {
prettyAssertEqual(actual, expected, msg);
}

/**
* Make an assertion that `actual` and `expected` are strictly equal. If
* not then throw.
*/
export function assertStrictEq(
actual: unknown,
expected: unknown,
msg?: string
): void {
if (actual !== expected) {
let actualString: string;
let expectedString: string;
try {
actualString = String(actual);
} catch (e) {
actualString = "[Cannot display]";
}
try {
expectedString = String(expected);
} catch (e) {
expectedString = "[Cannot display]";
}
console.error(
"strictEqual failed. actual =",
actualString,
"expected =",
expectedString
);
if (!msg) {
msg = `actual: ${actualString} expected: ${expectedString}`;
}
throw new Error(msg);
}
}

/**
* Make an assertion that actual contains expected. If not
* then thrown.
*/
export function assertStrContains(
actual: string,
expected: string,
msg?: string
): void {
if (!actual.includes(expected)) {
console.error(
"stringContains failed. actual =",
actual,
"not containing ",
expected
);
if (!msg) {
msg = `actual: "${actual}" expected to contains: "${expected}"`;
}
throw new Error(msg);
}
}

/**
* Make an assertion that `actual` match RegExp `expected`. If not
* then thrown
*/
export function assertMatch(
actual: string,
expected: RegExp,
msg?: string
): void {
if (!expected.test(actual)) {
console.error(
"stringMatching failed. actual =",
actual,
"not matching RegExp ",
expected
);
if (!msg) {
msg = `actual: "${actual}" expected to match: "${expected}"`;
}
throw new Error(msg);
}
}

/**
* Forcefully throws a failed assertion
*/
export function fail(msg?: string): void {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
assert(false, `Failed assertion${msg ? `: ${msg}` : "."}`);
}

/** Executes a function, expecting it to throw. If it does not, then it
* throws. An error class and a string that should be included in the
* error message can also be asserted.
*/
export function assertThrows(
fn: () => void,
ErrorClass?: Constructor,
msgIncludes = "",
msg?: string
): void {
let doesThrow = false;
try {
fn();
} catch (e) {
if (ErrorClass && !(Object.getPrototypeOf(e) === ErrorClass.prototype)) {
msg = `Expected error to be instance of "${ErrorClass.name}"${
msg ? `: ${msg}` : "."
}`;
throw new Error(msg);
}
if (msgIncludes) {
if (!e.message.includes(msgIncludes)) {
msg = `Expected error message to include "${msgIncludes}", but got "${
e.message
}"${msg ? `: ${msg}` : "."}`;
throw new Error(msg);
}
}
doesThrow = true;
}
if (!doesThrow) {
msg = `Expected function to throw${msg ? `: ${msg}` : "."}`;
throw new Error(msg);
}
}

export async function assertThrowsAsync(
fn: () => Promise<void>,
ErrorClass?: Constructor,
msgIncludes = "",
msg?: string
): Promise<void> {
let doesThrow = false;
try {
await fn();
} catch (e) {
if (ErrorClass && !(Object.getPrototypeOf(e) === ErrorClass.prototype)) {
msg = `Expected error to be instance of "${ErrorClass.name}"${
msg ? `: ${msg}` : "."
}`;
throw new Error(msg);
}
if (msgIncludes) {
if (!e.message.includes(msgIncludes)) {
msg = `Expected error message to include "${msgIncludes}", but got "${
e.message
}"${msg ? `: ${msg}` : "."}`;
throw new Error(msg);
}
}
doesThrow = true;
}
if (!doesThrow) {
msg = `Expected function to throw${msg ? `: ${msg}` : "."}`;
throw new Error(msg);
}
}
46 changes: 46 additions & 0 deletions testing/asserts_test.ts
@@ -0,0 +1,46 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.

import { assertStrContains, assertMatch } from "./asserts.ts";
import { test, assert } from "./mod.ts";
// import { assertEqual as prettyAssertEqual } from "./pretty.ts";
// import "./format_test.ts";
// import "./diff_test.ts";
// import "./pretty_test.ts";

test(function testingAssertStringContains() {
assertStrContains("Denosaurus", "saur");
assertStrContains("Denosaurus", "Deno");
assertStrContains("Denosaurus", "rus");
});

test(function testingAssertStringContainsThrow() {
let didThrow = false;
try {
assertStrContains("Denosaurus from Jurassic", "Raptor");
} catch (e) {
assert(
e.message ===
`actual: "Denosaurus from Jurassic" expected to contains: "Raptor"`
);
didThrow = true;
}
assert(didThrow);
});

test(function testingAssertStringMatching() {
assertMatch("foobar@deno.com", RegExp(/[a-zA-Z]+@[a-zA-Z]+.com/));
});

test(function testingAssertStringMatchingThrows() {
let didThrow = false;
try {
assertMatch("Denosaurus from Jurassic", RegExp(/Raptor/));
} catch (e) {
assert(
e.message ===
`actual: "Denosaurus from Jurassic" expected to match: "/Raptor/"`
);
didThrow = true;
}
assert(didThrow);
});
2 changes: 0 additions & 2 deletions testing/bench.ts
@@ -1,8 +1,6 @@
import { bench, runBenchmarks } from "./../benching/mod.ts";
import { runTests } from "./mod.ts";

import "./test.ts";

bench(async function testingSerial(b) {
b.start();
await runTests();
Expand Down
22 changes: 13 additions & 9 deletions testing/diff.ts
Expand Up @@ -4,7 +4,11 @@ interface FarthestPoint {
id: number;
}

export type DiffType = "removed" | "common" | "added";
export enum DiffType {
removed = "removed",
common = "common",
added = "added"
}

export interface DiffResult<T> {
type: DiffType;
Expand Down Expand Up @@ -50,12 +54,12 @@ export default function diff<T>(A: T[], B: T[]): Array<DiffResult<T>> {
if (!M && !N && !suffixCommon.length && !prefixCommon.length) return [];
if (!N) {
return [
...prefixCommon.map(c => ({ type: "common" as DiffType, value: c })),
...prefixCommon.map(c => ({ type: DiffType.common, value: c })),
...A.map(a => ({
type: (swapped ? "added" : "removed") as DiffType,
type: swapped ? DiffType.added : DiffType.removed,
value: a
})),
...suffixCommon.map(c => ({ type: "common" as DiffType, value: c }))
...suffixCommon.map(c => ({ type: DiffType.common, value: c }))
];
}
const offset = N;
Expand Down Expand Up @@ -91,18 +95,18 @@ export default function diff<T>(A: T[], B: T[]): Array<DiffResult<T>> {
const prev = j;
if (type === REMOVED) {
result.unshift({
type: (swapped ? "removed" : "added") as DiffType,
type: swapped ? DiffType.removed : DiffType.added,
value: B[b]
});
b -= 1;
} else if (type === ADDED) {
result.unshift({
type: (swapped ? "added" : "removed") as DiffType,
type: swapped ? DiffType.added : DiffType.removed,
value: A[a]
});
a -= 1;
} else {
result.unshift({ type: "common" as DiffType, value: A[a] });
result.unshift({ type: DiffType.common, value: A[a] });
a -= 1;
b -= 1;
}
Expand Down Expand Up @@ -194,8 +198,8 @@ export default function diff<T>(A: T[], B: T[]): Array<DiffResult<T>> {
);
}
return [
...prefixCommon.map(c => ({ type: "common" as DiffType, value: c })),
...prefixCommon.map(c => ({ type: DiffType.common, value: c })),
...backTrace(A, B, fp[delta + offset], swapped),
...suffixCommon.map(c => ({ type: "common" as DiffType, value: c }))
...suffixCommon.map(c => ({ type: DiffType.common, value: c }))
];
}

0 comments on commit c734e32

Please sign in to comment.