diff options
author | Vincent LE GOFF <g_n_s@hotmail.fr> | 2019-03-05 20:58:28 +0100 |
---|---|---|
committer | Ryan Dahl <ry@tinyclouds.org> | 2019-03-05 14:58:28 -0500 |
commit | 787207f11bfcd657b93b47f2ffeb457cdd6f4eb0 (patch) | |
tree | ac71143a059261805d4c5750a7254583714397d7 | |
parent | 39fde3a454b6bcc7daa6bca4fb7f4317550e9e58 (diff) |
Refactor asserts in testing (denoland/deno_std#227)
Original: https://github.com/denoland/deno_std/commit/c734e3234322cea5298a887373fe4ad1591d7c97
-rw-r--r-- | testing/asserts.ts | 179 | ||||
-rw-r--r-- | testing/asserts_test.ts | 46 | ||||
-rw-r--r-- | testing/bench.ts | 2 | ||||
-rw-r--r-- | testing/diff.ts | 22 | ||||
-rw-r--r-- | testing/mod.ts | 142 | ||||
-rw-r--r-- | testing/pretty.ts | 8 | ||||
-rw-r--r-- | testing/test.ts | 2 |
7 files changed, 261 insertions, 140 deletions
diff --git a/testing/asserts.ts b/testing/asserts.ts new file mode 100644 index 000000000..a2110b8d9 --- /dev/null +++ b/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); + } +} diff --git a/testing/asserts_test.ts b/testing/asserts_test.ts new file mode 100644 index 000000000..f4256d8f5 --- /dev/null +++ b/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); +}); diff --git a/testing/bench.ts b/testing/bench.ts index c5fd6b2a0..bc2e569d2 100644 --- a/testing/bench.ts +++ b/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(); diff --git a/testing/diff.ts b/testing/diff.ts index 4fab75e4a..e951032f5 100644 --- a/testing/diff.ts +++ b/testing/diff.ts @@ -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; @@ -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; @@ -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; } @@ -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 })) ]; } diff --git a/testing/mod.ts b/testing/mod.ts index 0f6ffefbc..c9cdde0d2 100644 --- a/testing/mod.ts +++ b/testing/mod.ts @@ -2,11 +2,16 @@ import { green, red } from "../colors/mod.ts"; import { assertEqual as prettyAssertEqual } from "./pretty.ts"; - -interface Constructor { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - new (...args: any[]): any; -} +import { + assert as assertImport, + equal as AssertEqual, + assertStrictEq, + assertStrContains, + assertMatch, + fail, + assertThrows, + assertThrowsAsync +} from "./asserts.ts"; export function equal(c: unknown, d: unknown): boolean { const seen = new Map(); @@ -36,125 +41,14 @@ export function equal(c: unknown, d: unknown): boolean { } const assertions = { - /** Make an assertion, if not `true`, then throw. */ - 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. - */ - 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. - */ - strictEqual(actual: unknown, expected: unknown, msg = ""): 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); - } - }, - - /** - * Forcefully throws a failed assertion - */ - 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. - */ - throws( - fn: () => void, - ErrorClass?: Constructor, - msgIncludes = "", - msg = "" - ): 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); - } - }, - - async throwsAsync( - fn: () => Promise<void>, - ErrorClass?: Constructor, - msgIncludes = "", - msg = "" - ): 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); - } - } + assert: assertImport, + equal: AssertEqual, + strictEqual: assertStrictEq, + assertStrContains: assertStrContains, + assertMatch: assertMatch, + fail: fail, + throws: assertThrows, + throwsAsync: assertThrowsAsync }; type Assert = typeof assertions.assert & typeof assertions; diff --git a/testing/pretty.ts b/testing/pretty.ts index 737d9c6dd..05fd86b6f 100644 --- a/testing/pretty.ts +++ b/testing/pretty.ts @@ -17,9 +17,9 @@ function createStr(v: unknown): string { function createColor(diffType: DiffType): (s: string) => string { switch (diffType) { - case "added": + case DiffType.added: return (s: string) => green(bold(s)); - case "removed": + case DiffType.removed: return (s: string) => red(bold(s)); default: return white; @@ -28,9 +28,9 @@ function createColor(diffType: DiffType): (s: string) => string { function createSign(diffType: DiffType): string { switch (diffType) { - case "added": + case DiffType.added: return "+ "; - case "removed": + case DiffType.removed: return "- "; default: return " "; diff --git a/testing/test.ts b/testing/test.ts index a90ab5dc7..bdebce431 100644 --- a/testing/test.ts +++ b/testing/test.ts @@ -1,10 +1,10 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. - import { test, assert, assertEqual, equal, runIfMain } from "./mod.ts"; import { assertEqual as prettyAssertEqual } from "./pretty.ts"; import "./format_test.ts"; import "./diff_test.ts"; import "./pretty_test.ts"; +import "./asserts_test.ts"; test(function testingEqual() { assert(equal("world", "world")); |