├── .gitignore ├── README.md ├── best ├── best.js └── bestFull.mjs ├── generators ├── generate.js ├── libs.js └── templates.js ├── jest.config.js ├── out ├── largeDiffErrors.ansi ├── mediumDiffErrors.ansi ├── results.md └── smallDiffErrors.ansi ├── package-lock.json ├── package.json ├── run.sh ├── scripts ├── aux.sh ├── diffErrors.sh └── utils.sh ├── src ├── employee.cjs └── employee.js ├── test ├── avaTest.js ├── baretestTest.js ├── bestTest.js ├── bunTest.js ├── jestTest.js ├── labTest.js ├── mochaTest.js ├── nativeTest.js ├── special │ ├── notestTest.js │ └── xvTest.js ├── tapTest.js ├── tapeTest.js ├── tehanuTest.js ├── uvuTest.js ├── vitestTest.js └── zoraTest.js └── vite.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | out/perfResults.txt 3 | .vscode 4 | tmp 5 | .tap 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## updated 2025-02-02, node 23.9.0 2 | - new PC 3 | - major simplification 4 | - using hyperfine for bench 5 | 6 | ## Cold start times 7 | 8 | `./run.sh perf` 9 | 10 | | ms | runner | ms | runner | 11 | | ---- | ----------- | -----: | ------------ | 12 | | 15.8 | [bun][46] | 134.8 | [tape][8] | 13 | | 56.0 | [notest][1] | 220.2 | [mocha][11] | 14 | | 57.4 | [best][2] | 259.5 | [lab][10] | 15 | | 60.4 | [tehanu][3] | 580.1 | [ava][13] | 16 | | 60.1 | [xv][6] | 654.4 | [vitest][45] | 17 | | 61.2 | [zora][5] | 748.0 | [jest][14] | 18 | | 66.3 | [uvu][7] | 1982.1 | [tap][12] | 19 | | 71.2 | [node][44] | | | 20 | 21 | ### Notes 22 | - some runners have no output, this makes them faster 23 | - **best** and **notest** are the fastest possible implementations, they are not actual libs. 24 | - **bun** is the fastest because it's native, written in Zig, while node is written in JS? 25 | - the slower runners use **hot reloading (HMR)**, cold start doesn't matter when using in watch mode 26 | - in **watch mode**, some runners achieve "flicker free" reload 27 | - checkout some popularity stats like number of stars, montly downloads, commit activity and others. [1][40], [2][43] 28 | 29 | I need to retest watch mode, but **mocha** was a perfect 10 for me. The fastest frameworks are nearly as good, and **vitest** is as well. Vitest achieves it's "flicker free" by replacing the terminal content instead of clearing it between reloads. 30 | 31 | 32 | ## My favorite runner 33 | I'm trying out **bun**, with a custom reporter. It is the **fastest** and has a **familiar syntax**. Also I'm a **minimalist**, libraries with too many features tend to focus on things I don't really need. My testing needs are simple. 34 | 35 | **Vitest** is the best one. It is the most active developed and the one with the brighter future, being backed by Evan You, the guy from Vue and VoidZero. 36 | 37 | ## Usage 38 | 1) clone 39 | 2) npm install 40 | 3) `./run.sh TARGET` 41 | 42 | Experiment by modifying `run.sh`. as need 43 | 44 | ### Examples 45 | ```sh 46 | ./run.sh perf 47 | ./run.sh mocha 48 | mode=equalError ./run.sh mocha # forces an assertion error 49 | ``` 50 | 51 | [Contributing](https://github.com/icetbr/my-projects/blob/main/CONTRIBUTING.md)\ 52 | [License (MIT)](https://choosealicense.com/licenses/mit/) 53 | 54 | 55 | [1]: test/employeeNotestTest.js 56 | [2]: best/best.js 57 | [3]: https://github.com/prantlf/tehanu 58 | [5]: https://github.com/lorenzofox3/zora 59 | [6]: https://github.com/typicode/xv 60 | [7]: https://github.com/lukeed/uvu 61 | [8]: https://github.com/substack/tape 62 | [10]: https://github.com/hapijs/lab 63 | [11]: https://github.com/mochajs/mocha 64 | [12]: https://github.com/tapjs/node-tap 65 | [13]: https://github.com/avajs/ava 66 | [14]: https://github.com/facebook/jest 67 | [40]: https://moiva.io/?npm=@hapi/lab+ava+jasmine+jest+mocha+tap+tape+tehanu+uvu+xv+zora 68 | [43]: https://npmtrends.com/@hapi/lab-vs-ava-vs-baretest-vs-jasmine-core-vs-jest-vs-mocha-vs-tap-vs-tape-vs-tehanu-vs-uvu-vs-zora 69 | [44]: https://nodejs.org/api/test.html 70 | [45]: https://vitest.dev/ 71 | [46]: https://bun.sh/docs/cli/test 72 | 73 | -------------------------------------------------------------------------------- /best/best.js: -------------------------------------------------------------------------------- 1 | export { deepStrictEqual as eq } from 'node:assert'; 2 | 3 | let tests = [] 4 | 5 | export const test = (name, fn) => { 6 | tests.push({ name, fn }) 7 | } 8 | 9 | async function run() { 10 | try { 11 | for (let i = 0; i < tests.length; i++) { 12 | await tests[i].fn() 13 | // console.log('✅', tests[i].name) 14 | } 15 | } catch (e) { 16 | // console.log('❌', t.name) 17 | console.log(e.stack) 18 | } 19 | } 20 | 21 | setImmediate(run) 22 | -------------------------------------------------------------------------------- /best/bestFull.mjs: -------------------------------------------------------------------------------- 1 | import { deepStrictEqual } from 'assert/strict'; 2 | 3 | const tests = []; 4 | const onlies = []; 5 | 6 | const green = msg => `\x1b[32m${msg}\x1b[0m`; 7 | const red = msg => `\x1b[31m${msg}\x1b[0m`; 8 | const purple = msg => `\x1b[35m${msg}\x1b[0m`; 9 | const dim = msg => `\x1b[2m${msg}\x1b[0m`; 10 | 11 | const print = m => process.stdout.write(m); 12 | 13 | const complete = startedAt => () => { 14 | const skip = onlies.length && tests.length - onlies.length; 15 | const passed = green(`${onlies.length || tests.length} tests`); 16 | const skipped = skip ? purple(` ${skip} skipped`) : ''; 17 | const duration = dim(` (${Date.now() - startedAt} ms) \n\n`); 18 | 19 | print(passed + skipped + duration); 20 | }; 21 | 22 | // const fail = name => e => print('❌', name, '\n', e.stack); 23 | const fail = name => e => { 24 | const messageLines = e.message.split('\n'); 25 | const firstFrame = e.stack.split('\n') 26 | .slice(messageLines.length, messageLines.length + 1)[0] 27 | .replace(`file://${process.cwd()}/`, ''); 28 | 29 | messageLines.splice(0, 2, red(name)); 30 | messageLines.unshift(''); 31 | 32 | messageLines.push(''); 33 | messageLines.push(dim(firstFrame.trim())); 34 | messageLines.push('\n'); 35 | 36 | print(messageLines.join('\n')) 37 | }; 38 | 39 | const aeq = (a, b) => { 40 | if (!(a && b && typeof a == 'object' && typeof b == 'object')) return; 41 | 42 | var length, i, keys; 43 | if (Array.isArray(a)) { 44 | length = a.length; 45 | for (i = length; i-- !== 0;) aeq(a, b) 46 | } 47 | 48 | keys = Object.keys(a); 49 | length = keys.length; 50 | 51 | for (i = length; i-- !== 0;) 52 | if (typeof b[key] === 'function') { 53 | const matcher = b[key](a[key]); 54 | b[key] = matcher.expected; 55 | a[key] = matcher.actual; 56 | return; 57 | } 58 | 59 | for (i = length; i-- !== 0;) aeq(a[keys[i]], b[keys[i]]) 60 | return eq(a, b) 61 | } 62 | 63 | const run = async ({ name, fn }) => 64 | Promise.resolve(fn()) 65 | .catch(fail(name)); 66 | 67 | const runAll = tests => () => 68 | Promise.all(tests.map(run)) 69 | .then(complete(Date.now())); 70 | 71 | setImmediate(runAll(onlies.length ? onlies : tests)); 72 | 73 | export const 74 | test = (name, fn) => tests.push({ name, fn }), 75 | only = (name, fn) => onlies.push({ name, fn }), 76 | eq = deepStrictEqual; -------------------------------------------------------------------------------- /generators/generate.js: -------------------------------------------------------------------------------- 1 | import { writeFileSync, readdirSync, rmSync, lstatSync } from 'fs'; 2 | import libs from './libs.js'; 3 | import templates from './templates.js'; 4 | 5 | // delete all files from ./test 6 | readdirSync('test/').filter(f => lstatSync(`test/${f}`).isFile()).forEach(f => rmSync(`test/${f}`)); 7 | 8 | console.log('generating tests \n') 9 | 10 | libs.forEach(lib => { 11 | writeFileSync(`test/${lib.name}Test.js`, templates[process.env.template](lib)); 12 | }); 13 | -------------------------------------------------------------------------------- /generators/libs.js: -------------------------------------------------------------------------------- 1 | import dedent from 'codedent'; 2 | 3 | export default [ 4 | { 5 | name: 'ava', 6 | imports: `import test from 'ava';`, 7 | assertParam: `{ deepEqual: eq }`, 8 | }, 9 | { 10 | name: 'baretest', 11 | imports: dedent` 12 | import baretest from 'baretest'; 13 | import { deepStrictEqual as eq } from 'node:assert'; 14 | const test = baretest('My app'); 15 | `, 16 | assertParam: '', 17 | runner: 'await test.run();' 18 | }, 19 | { 20 | name: 'best', 21 | imports: `import { test, eq } from '../best/best.js';`, 22 | }, 23 | { 24 | name: 'jest', 25 | assertion: 'expect(actual).toEqual(expected);', 26 | }, 27 | { 28 | name: 'lab', 29 | imports: dedent` 30 | import Code from '@hapi/code'; 31 | import Lab from '@hapi/lab'; 32 | import { expect } from '@hapi/code'; 33 | export const lab = Lab.script(); 34 | const { test } = lab; 35 | `, 36 | assertion: `expect(actual).to.equal(expected);`, 37 | runner: `Code.settings.truncateMessages = true;` // reduce trash from output 38 | }, 39 | { 40 | name: 'mocha', 41 | assertion: `expect(actual).to.deep.equal(expected);`, 42 | }, 43 | { 44 | name: 'tape', 45 | imports: `import { test } from 'tape';`, 46 | assertParam: `{ same: eq, end }`, 47 | endTest: `\n end();`, 48 | }, 49 | { 50 | name: 'tap', 51 | imports: `import { test } from 'tap';`, 52 | assertParam: `{ same: eq, end }`, 53 | endTest: `\n end();`, 54 | }, 55 | { 56 | name: 'tehanu', 57 | imports: dedent` 58 | import tehanu from 'tehanu'; 59 | const test = tehanu(''); 60 | import { deepStrictEqual as eq } from 'node:assert'; 61 | `, 62 | assertParam: ``, 63 | }, 64 | { 65 | name: 'uvu', 66 | imports: dedent` 67 | import { suite } from 'uvu'; 68 | import { equal as eq } from 'uvu/assert'; 69 | const test = suite('employee'); 70 | `, 71 | runner: 'await test.run();' 72 | }, 73 | { 74 | name: 'zora', 75 | imports: `import { test } from 'zora';`, 76 | assertParam: `{ equal: eq }`, 77 | }, 78 | { 79 | name: 'native', 80 | imports: dedent` 81 | import { test } from 'node:test'; 82 | import { deepStrictEqual as eq } from 'node:assert'; 83 | `, 84 | assertParam: ``, 85 | }, 86 | { 87 | name: 'vitest', 88 | imports: `import { expect, it, describe, test } from 'vitest';`, 89 | assertion: 'expect(actual).toEqual(expected);', 90 | }, 91 | { 92 | name: 'bun', 93 | imports: `import { expect, it, describe, test } from 'bun:test';`, 94 | assertion: 'expect(actual).toEqual(expected);', 95 | }, 96 | ]; 97 | -------------------------------------------------------------------------------- /generators/templates.js: -------------------------------------------------------------------------------- 1 | // import dedent from '@michaelray/dedent'; 2 | import dedent from 'dedent-js'; 3 | 4 | export default { 5 | baseTemplate: ({ imports = '', assertParam = '', assertion = 'eq(actual, expected);', runner = '', endTest = '' }) => dedent` 6 | import employee from '../src/employee.js'; 7 | ${imports} 8 | 9 | test('insert saves the data to the database', async (${assertParam}) => { 10 | const data = { name: 'John', email: 'john@test.com', description: 'average height' }; 11 | await employee.insert(data); 12 | 13 | const actual = await employee.find(); 14 | 15 | const expected = process.env.mode === 'equalError' ? [{ ...data, name: 'John1' }] : [data]; 16 | ${assertion} 17 | await employee.removeAll();\ 18 | ${endTest} 19 | }); 20 | 21 | ${runner} 22 | `, 23 | 24 | mediumDiffErrorTemplate: ({ imports = '', assertParam = '', assertion = 'eq(actual, expected);', runner = '', endTest = '' }) => dedent` 25 | ${imports} 26 | 27 | const game = () => ({ 28 | turn: { count: 1, player: 'within(1, 4)', phase: 'draw' }, 29 | drawPile: 'length(42)', 30 | discardPile: [], 31 | teams: [ 32 | { 33 | players: [ 34 | { name: 'Player 1', cards: 'length(11)', lastDraw: null }, 35 | { name: 'Player 2', cards: 'length(11)', lastDraw: null }, 36 | ], 37 | reservePile: 'length(11)', 38 | board: [], 39 | }, 40 | { 41 | players: [ 42 | { name: 'Player 3', cards: 'length(11)', lastDraw: null }, 43 | { name: 'Player 4', cards: 'length(11)', lastDraw: null }, 44 | ], 45 | reservePile: 'length(11)', 46 | board: [], 47 | } 48 | ], 49 | }); 50 | 51 | test('medium object diff', (${assertParam}) => { 52 | const actual = game(); 53 | const expected = game(); 54 | delete actual.teams[1].players[0].lastDraw; 55 | delete actual.teams[1].players[1].lastDraw; 56 | ${assertion}\ 57 | ${endTest} 58 | }); 59 | 60 | ${runner} 61 | `, 62 | 63 | largeDiffErrorTemplate: ({ imports = '', assertParam = '', assertion = 'eq(actual, expected);', runner = '', endTest = '' }) => dedent` 64 | const { times } = require('lodash'); 65 | ${imports} 66 | 67 | const deck = () => 68 | ['hearts', 'spades', 'clubs', 'diamonds'] 69 | .flatMap(suit => 70 | times(13, i => 71 | ({ rank: i + 1, suit }))) 72 | .concat({ rank: 1, suit: 'joker' }, { rank: 2, suit: 'joker' }); 73 | 74 | test('large object diff', (${assertParam}) => { 75 | const actual = deck(); 76 | const expected = deck(); 77 | actual[10].suit = 'bad suit' 78 | actual[30].suit = 'bad rank' 79 | ${assertion}\ 80 | ${endTest} 81 | }); 82 | 83 | ${runner} 84 | `, 85 | }; 86 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('jest').Config} */ 2 | export default { 3 | testMatch: ['/test/jestTest.js'], 4 | }; 5 | -------------------------------------------------------------------------------- /out/largeDiffErrors.ansi: -------------------------------------------------------------------------------- 1 |  2 | **************************************************************************************************************** 3 | jest 4 | **************************************************************************************************************** 5 |  6 | Determining test suites to run.... 7 |  ● large object diff 8 | 9 | expect(received).toEqual(expected) // deep equality 10 | 11 | - Expected - 2 12 | + Received + 2 13 | 14 | @@ -39,11 +39,11 @@ 15 |  "rank": 10, 16 |  "suit": "hearts", 17 |  }, 18 |  Object { 19 |  "rank": 11, 20 | - "suit": "hearts", 21 | + "suit": "bad suit", 22 |  }, 23 |  Object { 24 |  "rank": 12, 25 |  "suit": "hearts", 26 |  }, 27 | @@ -119,11 +119,11 @@ 28 |  "rank": 4, 29 |  "suit": "clubs", 30 |  }, 31 |  Object { 32 |  "rank": 5, 33 | - "suit": "clubs", 34 | + "suit": "bad rank", 35 |  }, 36 |  Object { 37 |  "rank": 6, 38 |  "suit": "clubs", 39 |  }, 40 |  41 |    14 | actual[10].suit = 'bad suit' 42 |    15 | actual[30].suit = 'bad rank' 43 |  > 16 | expect(actual).toEqual(expected);  44 |    | ^ 45 |    17 | }); 46 |    18 | 47 |    19 | 48 |  49 |  at Object. (test/jestTest.js:16:20) 50 | 51 | 52 | 53 |  54 | **************************************************************************************************************** 55 | ava 56 | **************************************************************************************************************** 57 |  58 | 59 | ✖ large object diff 60 | ─ 61 | 62 | large object diff 63 | 64 | test/avaTest.js:16 65 | 66 | 15: actual[30].suit = 'bad rank' 67 |  16: eq(actual, expected);  68 | 17: }); 69 | 70 | Difference: 71 | 72 | [ 73 | { 74 | rank: 1, 75 | suit: 'hearts', 76 | }, 77 | { 78 | rank: 2, 79 | suit: 'hearts', 80 | }, 81 | { 82 | rank: 3, 83 | suit: 'hearts', 84 | }, 85 | { 86 | rank: 4, 87 | suit: 'hearts', 88 | }, 89 | { 90 | rank: 5, 91 | suit: 'hearts', 92 | }, 93 | { 94 | rank: 6, 95 | suit: 'hearts', 96 | }, 97 | { 98 | rank: 7, 99 | suit: 'hearts', 100 | }, 101 | { 102 | rank: 8, 103 | suit: 'hearts', 104 | }, 105 | { 106 | rank: 9, 107 | suit: 'hearts', 108 | }, 109 | { 110 | rank: 10, 111 | suit: 'hearts', 112 | }, 113 | { 114 | rank: 11, 115 | - suit: 'bad suit', 116 | + suit: 'hearts', 117 | }, 118 | { 119 | rank: 12, 120 | suit: 'hearts', 121 | }, 122 | { 123 | rank: 13, 124 | suit: 'hearts', 125 | }, 126 | { 127 | rank: 1, 128 | suit: 'spades', 129 | }, 130 | { 131 | rank: 2, 132 | suit: 'spades', 133 | }, 134 | { 135 | rank: 3, 136 | suit: 'spades', 137 | }, 138 | { 139 | rank: 4, 140 | suit: 'spades', 141 | }, 142 | { 143 | rank: 5, 144 | suit: 'spades', 145 | }, 146 | { 147 | rank: 6, 148 | suit: 'spades', 149 | }, 150 | { 151 | rank: 7, 152 | suit: 'spades', 153 | }, 154 | { 155 | rank: 8, 156 | suit: 'spades', 157 | }, 158 | { 159 | rank: 9, 160 | suit: 'spades', 161 | }, 162 | { 163 | rank: 10, 164 | suit: 'spades', 165 | }, 166 | { 167 | rank: 11, 168 | suit: 'spades', 169 | }, 170 | { 171 | rank: 12, 172 | suit: 'spades', 173 | }, 174 | { 175 | rank: 13, 176 | suit: 'spades', 177 | }, 178 | { 179 | rank: 1, 180 | suit: 'clubs', 181 | }, 182 | { 183 | rank: 2, 184 | suit: 'clubs', 185 | }, 186 | { 187 | rank: 3, 188 | suit: 'clubs', 189 | }, 190 | { 191 | rank: 4, 192 | suit: 'clubs', 193 | }, 194 | { 195 | rank: 5, 196 | - suit: 'bad rank', 197 | + suit: 'clubs', 198 | }, 199 | { 200 | rank: 6, 201 | suit: 'clubs', 202 | }, 203 | { 204 | rank: 7, 205 | suit: 'clubs', 206 | }, 207 | { 208 | rank: 8, 209 | suit: 'clubs', 210 | }, 211 | { 212 | rank: 9, 213 | suit: 'clubs', 214 | }, 215 | { 216 | rank: 10, 217 | suit: 'clubs', 218 | }, 219 | { 220 | rank: 11, 221 | suit: 'clubs', 222 | }, 223 | { 224 | rank: 12, 225 | suit: 'clubs', 226 | }, 227 | { 228 | rank: 13, 229 | suit: 'clubs', 230 | }, 231 | { 232 | rank: 1, 233 | suit: 'diamonds', 234 | }, 235 | { 236 | rank: 2, 237 | suit: 'diamonds', 238 | }, 239 | { 240 | rank: 3, 241 | suit: 'diamonds', 242 | }, 243 | { 244 | rank: 4, 245 | suit: 'diamonds', 246 | }, 247 | { 248 | rank: 5, 249 | suit: 'diamonds', 250 | }, 251 | { 252 | rank: 6, 253 | suit: 'diamonds', 254 | }, 255 | { 256 | rank: 7, 257 | suit: 'diamonds', 258 | }, 259 | { 260 | rank: 8, 261 | suit: 'diamonds', 262 | }, 263 | { 264 | rank: 9, 265 | suit: 'diamonds', 266 | }, 267 | { 268 | rank: 10, 269 | suit: 'diamonds', 270 | }, 271 | { 272 | rank: 11, 273 | suit: 'diamonds', 274 | }, 275 | { 276 | rank: 12, 277 | suit: 'diamonds', 278 | }, 279 | { 280 | rank: 13, 281 | suit: 'diamonds', 282 | }, 283 | { 284 | rank: 1, 285 | suit: 'joker', 286 | }, 287 | { 288 | rank: 2, 289 | suit: 'joker', 290 | }, 291 | ] 292 | 293 | › test/avaTest.js:16:5 294 | 295 | ─ 296 | 297 | 1 test failed 298 |  299 | **************************************************************************************************************** 300 | mocha 301 | **************************************************************************************************************** 302 |  303 | (node:23310) [DEP0148] DeprecationWarning: Use of deprecated folder mapping "./" in the "exports" field module resolution of the package at /home/ddv/dev/projects/comparing-testing-libraries/node_modules/chai/package.json imported from /home/ddv/dev/projects/comparing-testing-libraries/node_modules/mocha/lib/nodejs/esm-utils.js. 304 | Update this package.json to use a subpath pattern like "./*". 305 | (Use `node --trace-deprecation ...` to show where the warning was created) 306 |  307 |   0 passing (6ms) 308 |  1 failing 309 | 310 |  1) large object diff: 311 | 312 | AssertionError: expected [ Array(54) ] to deeply equal [ Array(54) ] 313 | actual expected 314 | 315 | 1 | [ 316 | 2 | { 317 | 3 | "rank": 1 318 | 4 | "suit": "hearts" 319 | 5 | } 320 | 6 | { 321 | 7 | "rank": 2 322 | 8 | "suit": "hearts" 323 | 9 | } 324 | 10 | { 325 | 11 | "rank": 3 326 | 12 | "suit": "hearts" 327 | 13 | } 328 | 14 | { 329 | 15 | "rank": 4 330 | 16 | "suit": "hearts" 331 | 17 | } 332 | 18 | { 333 | 19 | "rank": 5 334 | 20 | "suit": "hearts" 335 | 21 | } 336 | 22 | { 337 | 23 | "rank": 6 338 | 24 | "suit": "hearts" 339 | 25 | } 340 | 26 | { 341 | 27 | "rank": 7 342 | 28 | "suit": "hearts" 343 | 29 | } 344 | 30 | { 345 | 31 | "rank": 8 346 | 32 | "suit": "hearts" 347 | 33 | } 348 | 34 | { 349 | 35 | "rank": 9 350 | 36 | "suit": "hearts" 351 | 37 | } 352 | 38 | { 353 | 39 | "rank": 10 354 | 40 | "suit": "hearts" 355 | 41 | } 356 | 42 | { 357 | 43 | "rank": 11 358 | 44 | "suit": "bad suithearts" 359 | 45 | } 360 | 46 | { 361 | 47 | "rank": 12 362 | 48 | "suit": "hearts" 363 | 49 | } 364 | 50 | { 365 | 51 | "rank": 13 366 | 52 | "suit": "hearts" 367 | 53 | } 368 | 54 | { 369 | 55 | "rank": 1 370 | 56 | "suit": "spades" 371 | 57 | } 372 | 58 | { 373 | 59 | "rank": 2 374 | 60 | "suit": "spades" 375 | 61 | } 376 | 62 | { 377 | 63 | "rank": 3 378 | 64 | "suit": "spades" 379 | 65 | } 380 | 66 | { 381 | 67 | "rank": 4 382 | 68 | "suit": "spades" 383 | 69 | } 384 | 70 | { 385 | 71 | "rank": 5 386 | 72 | "suit": "spades" 387 | 73 | } 388 | 74 | { 389 | 75 | "rank": 6 390 | 76 | "suit": "spades" 391 | 77 | } 392 | 78 | { 393 | 79 | "rank": 7 394 | 80 | "suit": "spades" 395 | 81 | } 396 | 82 | { 397 | 83 | "rank": 8 398 | 84 | "suit": "spades" 399 | 85 | } 400 | 86 | { 401 | 87 | "rank": 9 402 | 88 | "suit": "spades" 403 | 89 | } 404 | 90 | { 405 | 91 | "rank": 10 406 | 92 | "suit": "spades" 407 | 93 | } 408 | 94 | { 409 | 95 | "rank": 11 410 | 96 | "suit": "spades" 411 | 97 | } 412 | 98 | { 413 | 99 | "rank": 12 414 | 100 | "suit": "spades" 415 | 101 | } 416 | 102 | { 417 | 103 | "rank": 13 418 | 104 | "suit": "spades" 419 | 105 | } 420 | 106 | { 421 | 107 | "rank": 1 422 | 108 | "suit": "clubs" 423 | 109 | } 424 | 110 | { 425 | 111 | "rank": 2 426 | 112 | "suit": "clubs" 427 | 113 | } 428 | 114 | { 429 | 115 | "rank": 3 430 | 116 | "suit": "clubs" 431 | 117 | } 432 | 118 | { 433 | 119 | "rank": 4 434 | 120 | "suit": "clubs" 435 | 121 | } 436 | 122 | { 437 | 123 | "rank": 5 438 | 124 | "suit": "bad rankclubs" 439 | 125 | } 440 | 126 | { 441 | 127 | "rank": 6 442 | 128 | "suit": "clubs" 443 | 129 | } 444 | 130 | { 445 | 131 | "rank": 7 446 | 132 | "suit": "clubs" 447 | 133 | } 448 | 134 | { 449 | 135 | "rank": 8 450 | 136 | "suit": "clubs" 451 | 137 | } 452 | 138 | { 453 | 139 | "rank": 9 454 | 140 | "suit": "clubs" 455 | 141 | } 456 | 142 | { 457 | 143 | "rank": 10 458 | 144 | "suit": "clubs" 459 | 145 | } 460 | 146 | { 461 | 147 | "rank": 11 462 | 148 | "suit": "clubs" 463 | 149 | } 464 | 150 | { 465 | 151 | "rank": 12 466 | 152 | "suit": "clubs" 467 | 153 | } 468 | 154 | { 469 | 155 | "rank": 13 470 | 156 | "suit": "clubs" 471 | 157 | } 472 | 158 | { 473 | 159 | "rank": 1 474 | 160 | "suit": "diamonds" 475 | 161 | } 476 | 162 | { 477 | 163 | "rank": 2 478 | 164 | "suit": "diamonds" 479 | 165 | } 480 | 166 | { 481 | 167 | "rank": 3 482 | 168 | "suit": "diamonds" 483 | 169 | } 484 | 170 | { 485 | 171 | "rank": 4 486 | 172 | "suit": "diamonds" 487 | 173 | } 488 | 174 | { 489 | 175 | "rank": 5 490 | 176 | "suit": "diamonds" 491 | 177 | } 492 | 178 | { 493 | 179 | "rank": 6 494 | 180 | "suit": "diamonds" 495 | 181 | } 496 | 182 | { 497 | 183 | "rank": 7 498 | 184 | "suit": "diamonds" 499 | 185 | } 500 | 186 | { 501 | 187 | "rank": 8 502 | 188 | "suit": "diamonds" 503 | 189 | } 504 | 190 | { 505 | 191 |   "r ... Lines skipped 506 |  507 | at Context. (test/mochaTest.js:16:28) 508 | at processImmediate (node:internal/timers:464:21) 509 |  510 | 511 | 512 |  513 | **************************************************************************************************************** 514 | pta 515 | **************************************************************************************************************** 516 |  517 | 518 |  FAIL  large object diff 519 | at: /home/ddv/dev/projects/comparing-testing-libraries/test/zoraTest.js:16:5 520 | 521 | should be equivalent 522 | [equal] diff in objects: 523 |  - actual   + expected  524 | 525 |  [  526 |  {  527 |  "rank": 1,  528 |  "suit": "hearts"  529 |  },  530 |  {  531 |  "rank": 2,  532 |  "suit": "hearts"  533 |  },  534 |  {  535 |  "rank": 3,  536 |  "suit": "hearts"  537 |  },  538 |  {  539 |  "rank": 4,  540 |  "suit": "hearts"  541 |  },  542 |  {  543 |  "rank": 5,  544 |  "suit": "hearts"  545 |  },  546 |  {  547 |  "rank": 6,  548 |  "suit": "hearts"  549 |  },  550 |  {  551 |  "rank": 7,  552 |  "suit": "hearts"  553 |  },  554 |  {  555 |  "rank": 8,  556 |  "suit": "hearts"  557 |  },  558 |  {  559 |  "rank": 9,  560 |  "suit": "hearts"  561 |  },  562 |  {  563 |  "rank": 10,  564 |  "suit": "hearts"  565 |  },  566 |  {  567 |  "rank": 11,  568 |  -  "suit": "bad suit" 569 |  +  "suit": "hearts" 570 |  },  571 |  {  572 |  "rank": 12,  573 |  "suit": "hearts"  574 |  },  575 |  {  576 |  "rank": 13,  577 |  "suit": "hearts"  578 |  },  579 |  {  580 |  "rank": 1,  581 |  "suit": "spades"  582 |  },  583 |  {  584 |  "rank": 2,  585 |  "suit": "spades"  586 |  },  587 |  {  588 |  "rank": 3,  589 |  "suit": "spades"  590 |  },  591 |  {  592 |  "rank": 4,  593 |  "suit": "spades"  594 |  },  595 |  {  596 |  "rank": 5,  597 |  "suit": "spades"  598 |  },  599 |  {  600 |  "rank": 6,  601 |  "suit": "spades"  602 |  },  603 |  {  604 |  "rank": 7,  605 |  "suit": "spades"  606 |  },  607 |  {  608 |  "rank": 8,  609 |  "suit": "spades"  610 |  },  611 |  {  612 |  "rank": 9,  613 |  "suit": "spades"  614 |  },  615 |  {  616 |  "rank": 10,  617 |  "suit": "spades"  618 |  },  619 |  {  620 |  "rank": 11,  621 |  "suit": "spades"  622 |  },  623 |  {  624 |  "rank": 12,  625 |  "suit": "spades"  626 |  },  627 |  {  628 |  "rank": 13,  629 |  "suit": "spades"  630 |  },  631 |  {  632 |  "rank": 1,  633 |  "suit": "clubs"  634 |  },  635 |  {  636 |  "rank": 2,  637 |  "suit": "clubs"  638 |  },  639 |  {  640 |  "rank": 3,  641 |  "suit": "clubs"  642 |  },  643 |  {  644 |  "rank": 4,  645 |  "suit": "clubs"  646 |  },  647 |  {  648 |  "rank": 5,  649 |  -  "suit": "bad rank" 650 |  +  "suit": "clubs" 651 |  },  652 |  {  653 |  "rank": 6,  654 |  "suit": "clubs"  655 |  },  656 |  {  657 |  "rank": 7,  658 |  "suit": "clubs"  659 |  },  660 |  {  661 |  "rank": 8,  662 |  "suit": "clubs"  663 |  },  664 |  {  665 |  "rank": 9,  666 |  "suit": "clubs"  667 |  },  668 |  {  669 |  "rank": 10,  670 |  "suit": "clubs"  671 |  },  672 |  {  673 |  "rank": 11,  674 |  "suit": "clubs"  675 |  },  676 |  {  677 |  "rank": 12,  678 |  "suit": "clubs"  679 |  },  680 |  {  681 |  "rank": 13,  682 |  "suit": "clubs"  683 |  },  684 |  {  685 |  "rank": 1,  686 |  "suit": "diamonds"  687 |  },  688 |  {  689 |  "rank": 2,  690 |  "suit": "diamonds"  691 |  },  692 |  {  693 |  "rank": 3,  694 |  "suit": "diamonds"  695 |  },  696 |  {  697 |  "rank": 4,  698 |  "suit": "diamonds"  699 |  },  700 |  {  701 |  "rank": 5,  702 |  "suit": "diamonds"  703 |  },  704 |  {  705 |  "rank": 6,  706 |  "suit": "diamonds"  707 |  },  708 |  {  709 |  "rank": 7,  710 |  "suit": "diamonds"  711 |  },  712 |  {  713 |  "rank": 8,  714 |  "suit": "diamonds"  715 |  },  716 |  {  717 |  "rank": 9,  718 |  "suit": "diamonds"  719 |  },  720 |  {  721 |  "rank": 10,  722 |  "suit": "diamonds"  723 |  },  724 |  {  725 |  "rank": 11,  726 |  "suit": "diamonds"  727 |  },  728 |  {  729 |  "rank": 12,  730 |  "suit": "diamonds"  731 |  },  732 |  {  733 |  "rank": 13,  734 |  "suit": "diamonds"  735 |  },  736 |  {  737 |  "rank": 1,  738 |  "suit": "joker"  739 |  },  740 |  {  741 |  "rank": 2,  742 |  "suit": "joker"  743 |  }  744 |  ]  745 | 746 | TOTAL: 1 747 |  PASS: 0  748 |  FAIL: 1  749 |  SKIP: 0  750 | 751 |  752 | **************************************************************************************************************** 753 | best 754 | **************************************************************************************************************** 755 |  756 | AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: 757 | + actual - expected ... Lines skipped 758 | 759 | [ 760 | { 761 | ... 762 | { 763 | rank: 11, 764 | + suit: 'bad suit' 765 | - suit: 'hearts' 766 | }, 767 | { 768 | ... 769 | { 770 | rank: 5, 771 | + suit: 'bad rank' 772 | - suit: 'clubs' 773 | }, 774 | ... 775 | suit: 'joker' 776 | } 777 | ] 778 | at Object.fn (/home/ddv/dev/projects/comparing-testing-libraries/test/bestTest.js:16:5) 779 | at Immediate.run [as _onImmediate] (/home/ddv/dev/projects/comparing-testing-libraries/best/best.js:12:28) 780 | at processImmediate (node:internal/timers:464:21) 781 |  782 | **************************************************************************************************************** 783 | tapDiff2 784 | **************************************************************************************************************** 785 |  786 | 787 | large object diff 788 | ✖ should be deeply equivalent at Test. (/home/ddv/dev/projects/comparing-testing-libraries/test/tapeTest.js:16:5) 789 | [ 790 | { 791 | "rank": 1, 792 | "suit": "hearts" 793 | }, 794 | { 795 | "rank": 2, 796 | "suit": "hearts" 797 | }, 798 | { 799 | "rank": 3, 800 | "suit": "hearts" 801 | }, 802 | { 803 | "rank": 4, 804 | "suit": "hearts" 805 | }, 806 | { 807 | "rank": 5, 808 | "suit": "hearts" 809 | }, 810 | { 811 | "rank": 6, 812 | "suit": "hearts" 813 | }, 814 | { 815 | "rank": 7, 816 | "suit": "hearts" 817 | }, 818 | { 819 | "rank": 8, 820 | "suit": "hearts" 821 | }, 822 | { 823 | "rank": 9, 824 | "suit": "hearts" 825 | }, 826 | { 827 | "rank": 10, 828 | "suit": "hearts" 829 | }, 830 | { 831 | "rank": 11, 832 | "suit": "bad suithearts" 833 | }, 834 | { 835 | "rank": 12, 836 | "suit": "hearts" 837 | }, 838 | { 839 | "rank": 13, 840 | "suit": "hearts" 841 | }, 842 | { 843 | "rank": 1, 844 | "suit": "spades" 845 | }, 846 | { 847 | "rank": 2, 848 | "suit": "spades" 849 | }, 850 | { 851 | "rank": 3, 852 | "suit": "spades" 853 | }, 854 | { 855 | "rank": 4, 856 | "suit": "spades" 857 | }, 858 | { 859 | "rank": 5, 860 | "suit": "spades" 861 | }, 862 | { 863 | "rank": 6, 864 | "suit": "spades" 865 | }, 866 | { 867 | "rank": 7, 868 | "suit": "spades" 869 | }, 870 | { 871 | "rank": 8, 872 | "suit": "spades" 873 | }, 874 | { 875 | "rank": 9, 876 | "suit": "spades" 877 | }, 878 | { 879 | "rank": 10, 880 | "suit": "spades" 881 | }, 882 | { 883 | "rank": 11, 884 | "suit": "spades" 885 | }, 886 | { 887 | "rank": 12, 888 | "suit": "spades" 889 | }, 890 | { 891 | "rank": 13, 892 | "suit": "spades" 893 | }, 894 | { 895 | "rank": 1, 896 | "suit": "clubs" 897 | }, 898 | { 899 | "rank": 2, 900 | "suit": "clubs" 901 | }, 902 | { 903 | "rank": 3, 904 | "suit": "clubs" 905 | }, 906 | { 907 | "rank": 4, 908 | "suit": "clubs" 909 | }, 910 | { 911 | "rank": 5, 912 | "suit": "bad rankclubs" 913 | }, 914 | { 915 | "rank": 6, 916 | "suit": "clubs" 917 | }, 918 | { 919 | "rank": 7, 920 | "suit": "clubs" 921 | }, 922 | { 923 | "rank": 8, 924 | "suit": "clubs" 925 | }, 926 | { 927 | "rank": 9, 928 | "suit": "clubs" 929 | }, 930 | { 931 | "rank": 10, 932 | "suit": "clubs" 933 | }, 934 | { 935 | "rank": 11, 936 | "suit": "clubs" 937 | }, 938 | { 939 | "rank": 12, 940 | "suit": "clubs" 941 | }, 942 | { 943 | "rank": 13, 944 | "suit": "clubs" 945 | }, 946 | { 947 | "rank": 1, 948 | "suit": "diamonds" 949 | }, 950 | { 951 | "rank": 2, 952 | "suit": "diamonds" 953 | }, 954 | { 955 | "rank": 3, 956 | "suit": "diamonds" 957 | }, 958 | { 959 | "rank": 4, 960 | "suit": "diamonds" 961 | }, 962 | { 963 | "rank": 5, 964 | "suit": "diamonds" 965 | }, 966 | { 967 | "rank": 6, 968 | "suit": "diamonds" 969 | }, 970 | { 971 | "rank": 7, 972 | "suit": "diamonds" 973 | }, 974 | { 975 | "rank": 8, 976 | "suit": "diamonds" 977 | }, 978 | { 979 | "rank": 9, 980 | "suit": "diamonds" 981 | }, 982 | { 983 | "rank": 10, 984 | "suit": "diamonds" 985 | }, 986 | { 987 | "rank": 11, 988 | "suit": "diamonds" 989 | }, 990 | { 991 | "rank": 12, 992 | "suit": "diamonds" 993 | }, 994 | { 995 | "rank": 13, 996 | "suit": "diamonds" 997 | }, 998 | { 999 | "rank": 1, 1000 | "suit": "joker" 1001 | }, 1002 | { 1003 | "rank": 2, 1004 | "suit": "joker" 1005 | } 1006 | ] 1007 | 1008 | passed: 0 failed: 1 of 1 tests (30ms) 1009 | 1010 | 1 of 1 tests failed. 1011 | 1012 |  1013 | **************************************************************************************************************** 1014 | tapArc 1015 | **************************************************************************************************************** 1016 |  1017 | 1018 |  large object diff 1019 |   ✖ 1) should be deeply equivalent 1020 |    [ 1021 |    ··{ 1022 |    ····"rank": 1, 1023 |    ····"suit": "hearts" 1024 |    ··}, 1025 |    ··{ 1026 |    ····"rank": 2, 1027 |    ····"suit": "hearts" 1028 |    ··}, 1029 |    ··{ 1030 |    ····"rank": 3, 1031 |    ····"suit": "hearts" 1032 |    ··}, 1033 |    ··{ 1034 |    ····"rank": 4, 1035 |    ····"suit": "hearts" 1036 |    ··}, 1037 |    ··{ 1038 |    ····"rank": 5, 1039 |    ····"suit": "hearts" 1040 |    ··}, 1041 |    ··{ 1042 |    ····"rank": 6, 1043 |    ····"suit": "hearts" 1044 |    ··}, 1045 |    ··{ 1046 |    ····"rank": 7, 1047 |    ····"suit": "hearts" 1048 |    ··}, 1049 |    ··{ 1050 |    ····"rank": 8, 1051 |    ····"suit": "hearts" 1052 |    ··}, 1053 |    ··{ 1054 |    ····"rank": 9, 1055 |    ····"suit": "hearts" 1056 |    ··}, 1057 |    ··{ 1058 |    ····"rank": 10, 1059 |    ····"suit": "hearts" 1060 |    ··}, 1061 |    ··{ 1062 |    ····"rank": 11, 1063 |    ····"suit": "bhead rtsuit" 1064 |    ··}, 1065 |    ··{ 1066 |    ····"rank": 12, 1067 |    ····"suit": "hearts" 1068 |    ··}, 1069 |    ··{ 1070 |    ····"rank": 13, 1071 |    ····"suit": "hearts" 1072 |    ··}, 1073 |    ··{ 1074 |    ····"rank": 1, 1075 |    ····"suit": "spades" 1076 |    ··}, 1077 |    ··{ 1078 |    ····"rank": 2, 1079 |    ····"suit": "spades" 1080 |    ··}, 1081 |    ··{ 1082 |    ····"rank": 3, 1083 |    ····"suit": "spades" 1084 |    ··}, 1085 |    ··{ 1086 |    ····"rank": 4, 1087 |    ····"suit": "spades" 1088 |    ··}, 1089 |    ··{ 1090 |    ····"rank": 5, 1091 |    ····"suit": "spades" 1092 |    ··}, 1093 |    ··{ 1094 |    ····"rank": 6, 1095 |    ····"suit": "spades" 1096 |    ··}, 1097 |    ··{ 1098 |    ····"rank": 7, 1099 |    ····"suit": "spades" 1100 |    ··}, 1101 |    ··{ 1102 |    ····"rank": 8, 1103 |    ····"suit": "spades" 1104 |    ··}, 1105 |    ··{ 1106 |    ····"rank": 9, 1107 |    ····"suit": "spades" 1108 |    ··}, 1109 |    ··{ 1110 |    ····"rank": 10, 1111 |    ····"suit": "spades" 1112 |    ··}, 1113 |    ··{ 1114 |    ····"rank": 11, 1115 |    ····"suit": "spades" 1116 |    ··}, 1117 |    ··{ 1118 |    ····"rank": 12, 1119 |    ····"suit": "spades" 1120 |    ··}, 1121 |    ··{ 1122 |    ····"rank": 13, 1123 |    ····"suit": "spades" 1124 |    ··}, 1125 |    ··{ 1126 |    ····"rank": 1, 1127 |    ····"suit": "clubs" 1128 |    ··}, 1129 |    ··{ 1130 |    ····"rank": 2, 1131 |    ····"suit": "clubs" 1132 |    ··}, 1133 |    ··{ 1134 |    ····"rank": 3, 1135 |    ····"suit": "clubs" 1136 |    ··}, 1137 |    ··{ 1138 |    ····"rank": 4, 1139 |    ····"suit": "clubs" 1140 |    ··}, 1141 |    ··{ 1142 |    ····"rank": 5, 1143 |    ····"suit": "clubad ranks" 1144 |    ··}, 1145 |    ··{ 1146 |    ····"rank": 6, 1147 |    ····"suit": "clubs" 1148 |    ··}, 1149 |    ··{ 1150 |    ····"rank": 7, 1151 |    ····"suit": "clubs" 1152 |    ··}, 1153 |    ··{ 1154 |    ····"rank": 8, 1155 |    ····"suit": "clubs" 1156 |    ··}, 1157 |    ··{ 1158 |    ····"rank": 9, 1159 |    ····"suit": "clubs" 1160 |    ··}, 1161 |    ··{ 1162 |    ····"rank": 10, 1163 |    ····"suit": "clubs" 1164 |    ··}, 1165 |    ··{ 1166 |    ····"rank": 11, 1167 |    ····"suit": "clubs" 1168 |    ··}, 1169 |    ··{ 1170 |    ····"rank": 12, 1171 |    ····"suit": "clubs" 1172 |    ··}, 1173 |    ··{ 1174 |    ····"rank": 13, 1175 |    ····"suit": "clubs" 1176 |    ··}, 1177 |    ··{ 1178 |    ····"rank": 1, 1179 |    ····"suit": "diamonds" 1180 |    ··}, 1181 |    ··{ 1182 |    ····"rank": 2, 1183 |    ····"suit": "diamonds" 1184 |    ··}, 1185 |    ··{ 1186 |    ····"rank": 3, 1187 |    ····"suit": "diamonds" 1188 |    ··}, 1189 |    ··{ 1190 |    ····"rank": 4, 1191 |    ····"suit": "diamonds" 1192 |    ··}, 1193 |    ··{ 1194 |    ····"rank": 5, 1195 |    ····"suit": "diamonds" 1196 |    ··}, 1197 |    ··{ 1198 |    ····"rank": 6, 1199 |    ····"suit": "diamonds" 1200 |    ··}, 1201 |    ··{ 1202 |    ····"rank": 7, 1203 |    ····"suit": "diamonds" 1204 |    ··}, 1205 |    ··{ 1206 |    ····"rank": 8, 1207 |    ····"suit": "diamonds" 1208 |    ··}, 1209 |    ··{ 1210 |    ····"rank": 9, 1211 |    ····"suit": "diamonds" 1212 |    ··}, 1213 |    ··{ 1214 |    ····"rank": 10, 1215 |    ····"suit": "diamonds" 1216 |    ··}, 1217 |    ··{ 1218 |    ····"rank": 11, 1219 |    ····"suit": "diamonds" 1220 |    ··}, 1221 |    ··{ 1222 |    ····"rank": 12, 1223 |    ····"suit": "diamonds" 1224 |    ··}, 1225 |    ··{ 1226 |    ····"rank": 13, 1227 |    ····"suit": "diamonds" 1228 |    ··}, 1229 |    ··{ 1230 |    ····"rank": 1, 1231 |    ····"suit": "joker" 1232 |    ··}, 1233 |    ··{ 1234 |    ····"rank": 2, 1235 |    ····"suit": "joker" 1236 |    ··} 1237 |    ] 1238 |    At: Test. (/test/tapeTest.js:16:5) 1239 |     1240 | 1241 |  Failed tests: There was 1 failure 1242 | 1243 |   ✖ 1) should be deeply equivalent 1244 | 1245 |  total: 1 1246 |  failing: 1 1247 |  55 ms 1248 | 1249 |  1250 | **************************************************************************************************************** 1251 | tapDifflet 1252 | **************************************************************************************************************** 1253 |  1254 | 1255 | large object diff 1256 | ⨯ should be deeply equivalent 1257 |  1258 | [ { rank: 1, suit: 'hearts' }, { rank: 2, suit: 'hearts' }, { rank: 3, suit: 'hearts' }, { rank: 4, suit: 'hearts' }, { rank: 5, suit: 'hearts' }, { rank: 6, suit: 'hearts' }, { rank: 7, suit: 'hearts' }, { rank: 8, suit: 'hearts' }, { rank: 9, suit: 'hearts' }, { rank: 10, suit: 'hearts' }, { rank: 11, suit: 'bhead suirts' }, { rank: 12, suit: 'hearts' }, { rank: 13, suit: 'hearts' }, { rank: 1, suit: 'spades' }, { rank: 2, suit: 'spades' }, { rank: 3, suit: 'spades' }, { rank: 4, suit: 'spades' }, { rank: 5, suit: 'spades' }, { rank: 6, suit: 'spades' }, { rank: 7, suit: 'spades' }, { rank: 8, suit: 'spades' }, { rank: 9, suit: 'spades' }, { rank: 10, suit: 'spades' }, { rank: 11, suit: 'spades' }, { rank: 12, suit: 'spades' }, { rank: 13, suit: 'spades' }, { rank: 1, suit: 'clubs' }, { rank: 2, suit: 'clubs' }, { rank: 3, suit: 'clubs' }, { rank: 4, suit: 'clubs' }, { rank: 5, suit: 'clubad ranks' }, { rank: 6, suit: 'clubs' }, { rank: 7, suit: 'clubs' }, { rank: 8, suit: 'clubs' }, { rank: 9, suit: 'clubs' }, { rank: 10, suit: 'clubs' }, { rank: 11, suit: 'clubs' }, { rank: 12, suit: 'clubs' }, { rank: 13, suit: 'clubs' }, { rank: 1, suit: 'diamonds' }, { rank: 2, suit: 'diamonds' }, { rank: 3, suit: 'diamonds' }, { rank: 4, suit: 'diamonds' }, { rank: 5, suit: 'diamonds' }, { rank: 6, suit: 'diamonds' }, { rank: 7, suit: 'diamonds' }, { rank: 8, suit: 'diamonds' }, { rank: 9, suit: 'diamonds' }, { rank: 10, suit: 'diamonds' }, { rank: 11, suit: 'diamonds' }, { rank: 12, suit: 'diamonds' }, { rank: 13, suit: 'diamonds' }, { rank: 1, suit: 'joker' }, { rank: 2, suit: 'joker' } ] 1259 | 1260 | At: Test. (/home/ddv/dev/projects/comparing-testing-libraries/test/tapeTest.js:16:5) 1261 | 1262 | 1 failing 1263 | 0 passing (34ms) 1264 | 1265 |  1266 | **************************************************************************************************************** 1267 | tapNirvana 1268 | **************************************************************************************************************** 1269 |  1270 | 1271 | large object diff 1272 | ✖ should be deeply equivalent 1273 | ------------------------------- 1274 | operator: deepEqual 1275 | expected: [ 1276 | {"rank": 1, "suit": "hearts"}, 1277 | {"rank": 2, "suit": "hearts"}, 1278 | {"rank": 3, "suit": "hearts"}, 1279 | {"rank": 4, "suit": "hearts"}, 1280 | {"rank": 5, "suit": "hearts"}, 1281 | {"rank": 6, "suit": "hearts"}, 1282 | {"rank": 7, "suit": "hearts"}, 1283 | {"rank": 8, "suit": "hearts"}, 1284 | {"rank": 9, "suit": "hearts"}, 1285 | {"rank": 10, "suit": "hearts"}, 1286 | {"rank": 11, "suit": "hearts"}, 1287 | {"rank": 12, "suit": "hearts"}, 1288 | {"rank": 13, "suit": "hearts"}, 1289 | {"rank": 1, "suit": "spades"}, 1290 | {"rank": 2, "suit": "spades"}, 1291 | {"rank": 3, "suit": "spades"}, 1292 | {"rank": 4, "suit": "spades"}, 1293 | {"rank": 5, "suit": "spades"}, 1294 | {"rank": 6, "suit": "spades"}, 1295 | {"rank": 7, "suit": "spades"}, 1296 | {"rank": 8, "suit": "spades"}, 1297 | {"rank": 9, "suit": "spades"}, 1298 | {"rank": 10, "suit": "spades"}, 1299 | {"rank": 11, "suit": "spades"}, 1300 | {"rank": 12, "suit": "spades"}, 1301 | {"rank": 13, "suit": "spades"}, 1302 | {"rank": 1, "suit": "clubs"}, 1303 | {"rank": 2, "suit": "clubs"}, 1304 | {"rank": 3, "suit": "clubs"}, 1305 | {"rank": 4, "suit": "clubs"}, 1306 | {"rank": 5, "suit": "clubs"}, 1307 | {"rank": 6, "suit": "clubs"}, 1308 | {"rank": 7, "suit": "clubs"}, 1309 | {"rank": 8, "suit": "clubs"}, 1310 | {"rank": 9, "suit": "clubs"}, 1311 | {"rank": 10, "suit": "clubs"}, 1312 | {"rank": 11, "suit": "clubs"}, 1313 | {"rank": 12, "suit": "clubs"}, 1314 | {"rank": 13, "suit": "clubs"}, 1315 | {"rank": 1, "suit": "diamonds"}, 1316 | {"rank": 2, "suit": "diamonds"}, 1317 | {"rank": 3, "suit": "diamonds"}, 1318 | {"rank": 4, "suit": "diamonds"}, 1319 | {"rank": 5, "suit": "diamonds"}, 1320 | {"rank": 6, "suit": "diamonds"}, 1321 | {"rank": 7, "suit": "diamonds"}, 1322 | {"rank": 8, "suit": "diamonds"}, 1323 | {"rank": 9, "suit": "diamonds"}, 1324 | {"rank": 10, "suit": "diamonds"}, 1325 | {"rank": 11, "suit": "diamonds"}, 1326 | {"rank": 12, "suit": "diamonds"}, 1327 | {"rank": 13, "suit": "diamonds"}, 1328 | {"rank": 1, "suit": "joker"}, 1329 | {"rank": 2, "suit": "joker"} 1330 | ] 1331 | diff: [ 1332 | 10: { 1333 | suit: "hearts" => "bad suit" 1334 | } 1335 | 30: { 1336 | suit: "clubs" => "bad rank" 1337 | } 1338 | ] 1339 | source: at Test. (/home/ddv/dev/projects/comparing-testing-libraries/test/tapeTest.js:16:5) 1340 | at processImmediate (node:internal/timers:464:21) 1341 |  1342 | 1343 | 1344 | 1345 | 1346 | passed: 0, failed: 1 of 1 tests (32ms) 1347 | -------------------------------------------------------------------------------- /out/mediumDiffErrors.ansi: -------------------------------------------------------------------------------- 1 |  2 | **************************************************************************************************************** 3 | jest 4 | **************************************************************************************************************** 5 |  6 | Determining test suites to run.... 7 |  ● medium object diff 8 | 9 | expect(received).toEqual(expected) // deep equality 10 | 11 | - Expected - 2 12 | + Received + 0 13 | 14 | @@ -21,16 +21,14 @@ 15 |  Object { 16 |  "board": Array [], 17 |  "players": Array [ 18 |  Object { 19 |  "cards": "length(11)", 20 | - "lastDraw": null, 21 |  "name": "Player 3", 22 |  }, 23 |  Object { 24 |  "cards": "length(11)", 25 | - "lastDraw": null, 26 |  "name": "Player 4", 27 |  }, 28 |  ], 29 |  "reservePile": "length(11)", 30 |  }, 31 |  32 |    30 | delete actual.teams[1].players[0].lastDraw; 33 |    31 | delete actual.teams[1].players[1].lastDraw; 34 |  > 32 | expect(actual).toEqual(expected);  35 |    | ^ 36 |    33 | }); 37 |    34 | 38 |    35 | 39 |  40 |  at Object. (test/jestTest.js:32:20) 41 | 42 | 43 | 44 |  45 | **************************************************************************************************************** 46 | ava 47 | **************************************************************************************************************** 48 |  49 | 50 | ✖ medium object diff 51 | ─ 52 | 53 | medium object diff 54 | 55 | test/avaTest.js:32 56 | 57 | 31: delete actual.teams[1].players[1].lastDraw; 58 |  32: eq(actual, expected);  59 | 33: }); 60 | 61 | Difference: 62 | 63 | { 64 | discardPile: [], 65 | drawPile: 'length(42)', 66 | teams: [ 67 | { 68 | board: [], 69 | players: Array [ … ], 70 | reservePile: 'length(11)', 71 | }, 72 | { 73 | board: [], 74 | players: [ 75 | { 76 | cards: 'length(11)', 77 | + lastDraw: null, 78 | name: 'Player 3', 79 | }, 80 | { 81 | cards: 'length(11)', 82 | + lastDraw: null, 83 | name: 'Player 4', 84 | }, 85 | ], 86 | reservePile: 'length(11)', 87 | }, 88 | ], 89 | turn: { 90 | count: 1, 91 | phase: 'draw', 92 | player: 'within(1, 4)', 93 | }, 94 | } 95 | 96 | › test/avaTest.js:32:5 97 | 98 | ─ 99 | 100 | 1 test failed 101 |  102 | **************************************************************************************************************** 103 | mocha 104 | **************************************************************************************************************** 105 |  106 | (node:24223) [DEP0148] DeprecationWarning: Use of deprecated folder mapping "./" in the "exports" field module resolution of the package at /home/ddv/dev/projects/comparing-testing-libraries/node_modules/chai/package.json imported from /home/ddv/dev/projects/comparing-testing-libraries/node_modules/mocha/lib/nodejs/esm-utils.js. 107 | Update this package.json to use a subpath pattern like "./*". 108 | (Use `node --trace-deprecation ...` to show where the warning was created) 109 |  110 |   0 passing (4ms) 111 |  1 failing 112 | 113 |  1) medium object diff: 114 | 115 | AssertionError: expected { Object (turn, drawPile, ...) } to deeply equal { Object (turn, drawPile, ...) } 116 | actual expected 117 | 118 | 1 | { 119 | 2 | "discardPile": [] 120 | 3 | "drawPile": "length(42)" 121 | 4 | "teams": [ 122 | 5 | { 123 | 6 | "board": [] 124 | 7 | "players": [ 125 | 8 | { 126 | 9 | "cards": "length(11)" 127 | 10 | "lastDraw": [null] 128 | 11 | "name": "Player 1" 129 | 12 | } 130 | 13 | { 131 | 14 | "cards": "length(11)" 132 | 15 | "lastDraw": [null] 133 | 16 | "name": "Player 2" 134 | 17 | } 135 | 18 | ] 136 | 19 | "reservePile": "length(11)" 137 | 20 | } 138 | 21 | { 139 | 22 | "board": [] 140 | 23 | "players": [ 141 | 24 | { 142 | 25 | "cards": "length(11)" 143 | 26 | "lastDraw": [null] 144 | 27 |  "name": "Player 3" 145 | 28 | } 146 | 29 | { 147 | 30 | "cards": "length(11)" 148 | 31 | "lastDraw": [null] 149 | 32 |  "name": "Player 4" 150 | 33 | } 151 | 34 | ] 152 | 35 | "reservePile": "length(11)" 153 | 36 | } 154 | 37 | ] 155 | 38 | "turn": { 156 | 39 | "count": 1 157 | 40 | "phase": "draw" 158 | 41 | "player": "within(1, 4)" 159 | 42 | } 160 | 43 | } 161 |  162 | at Context. (test/mochaTest.js:32:28) 163 | at processImmediate (node:internal/timers:464:21) 164 |  165 | 166 | 167 |  168 | **************************************************************************************************************** 169 | lab 170 | **************************************************************************************************************** 171 |  172 | 173 | 174 | x 175 | 176 | Failed tests: 177 | 178 | 1) medium object diff: 179 | 180 | actual expected 181 | 182 | { 183 | discardPile: [], 184 | drawPile: 'length(42)', 185 | teams: [ 186 | { 187 | board: [], 188 | players: [ 189 | { 190 | cards: 'length(11)', 191 | lastDraw: null, 192 | name: 'Player 1' 193 | }, 194 | { 195 | cards: 'length(11)', 196 | lastDraw: null, 197 | name: 'Player 2' 198 | } 199 | ], 200 | reservePile: 'length(11)' 201 | }, 202 | { 203 | board: [], 204 | players: [ 205 | { 206 | cards: 'length(11)', 207 | lastDraw: null, 208 |  name: 'Player 3' 209 | }, 210 | { 211 | cards: 'length(11)', 212 | lastDraw: null, 213 |  name: 'Player 4' 214 | } 215 | ], 216 | reservePile: 'length(11)' 217 | } 218 | ], 219 | turn: { 220 | count: 1, 221 | phase: 'draw', 222 | player: 'within(1, 4)' 223 | } 224 | } 225 | 226 | Expected { Object (turn, drawPile, ...) } to equal specified value: { Object (turn, drawPile, ...) } 227 | 228 |  at /home/ddv/dev/projects/comparing-testing-libraries/test/labTest.js:34:23 229 | 230 | 231 | 1 of 1 tests failed 232 | Test duration: 6 ms 233 | Leaks: No issues 234 | 235 |  236 | **************************************************************************************************************** 237 | pta 238 | **************************************************************************************************************** 239 |  240 | 241 |  FAIL  medium object diff 242 | at: /home/ddv/dev/projects/comparing-testing-libraries/test/zoraTest.js:32:5 243 | 244 | should be equivalent 245 | [equal] diff in objects: 246 |  - actual   + expected  247 | 248 |  {  249 |  "discardPile": [],  250 |  "drawPile": "length(42)",  251 |  "teams": [  252 |  {  253 |  "board": [],  254 |  "players": [  255 |  {  256 |  "cards": "length(11)",  257 |  "lastDraw": null,  258 |  "name": "Player 1"  259 |  },  260 |  {  261 |  "cards": "length(11)",  262 |  "lastDraw": null,  263 |  "name": "Player 2"  264 |  }  265 |  ],  266 |  "reservePile": "length(11)"  267 |  },  268 |  {  269 |  "board": [],  270 |  "players": [  271 |  {  272 |  "cards": "length(11)",  273 |  +  "lastDraw": null, 274 |  "name": "Player 3"  275 |  },  276 |  {  277 |  "cards": "length(11)",  278 |  +  "lastDraw": null, 279 |  "name": "Player 4"  280 |  }  281 |  ],  282 |  "reservePile": "length(11)"  283 |  }  284 |  ],  285 |  "turn": {  286 |  "count": 1,  287 |  "phase": "draw",  288 |  "player": "within(1, 4)"  289 |  }  290 |  }  291 | 292 | TOTAL: 1 293 |  PASS: 0  294 |  FAIL: 1  295 |  SKIP: 0  296 | 297 |  298 | **************************************************************************************************************** 299 | best 300 | **************************************************************************************************************** 301 |  302 | AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: 303 | + actual - expected ... Lines skipped 304 | 305 | { 306 | discardPile: [], 307 | ... 308 | { 309 | cards: 'length(11)', 310 | + name: 'Player 3' 311 | + }, 312 | + { 313 | + cards: 'length(11)', 314 | - lastDraw: null, 315 | - name: 'Player 3' 316 | - }, 317 | - { 318 | - cards: 'length(11)', 319 | - lastDraw: null, 320 | name: 'Player 4' 321 | ... 322 | player: 'within(1, 4)' 323 | } 324 | } 325 | at Object.fn (/home/ddv/dev/projects/comparing-testing-libraries/test/bestTest.js:32:5) 326 | at Immediate.run [as _onImmediate] (/home/ddv/dev/projects/comparing-testing-libraries/best/best.js:12:28) 327 | at processImmediate (node:internal/timers:464:21) 328 |  329 | **************************************************************************************************************** 330 | tapDiff2 331 | **************************************************************************************************************** 332 |  333 | 334 | medium object diff 335 | ✖ should be deeply equivalent at Test. (/home/ddv/dev/projects/comparing-testing-libraries/test/tapeTest.js:32:5) 336 | { 337 | "discardPile": [ 338 | ], 339 | "drawPile": "length(42)", 340 | "teams": [ 341 | { 342 | "board": [ 343 | ], 344 | "players": [ 345 | { 346 | "cards": "length(11)", 347 | "lastDraw": null, 348 | "name": "Player 1" 349 | }, 350 | { 351 | "cards": "length(11)", 352 | "lastDraw": null, 353 | "name": "Player 2" 354 | } 355 | ], 356 | "reservePile": "length(11)" 357 | }, 358 | { 359 | "board": [ 360 | ], 361 | "players": [ 362 | { 363 | "cards": "length(11)", 364 | "lastDraw": null, 365 | "name": "Player 3" 366 | }, 367 | { 368 | "cards": "length(11)", 369 | "lastDraw": null, 370 | "name": "Player 4" 371 | } 372 | ], 373 | "reservePile": "length(11)" 374 | } 375 | ], 376 | "turn": { 377 | "count": 1, 378 | "phase": "draw", 379 | "player": "within(1, 4)" 380 | } 381 | } 382 | 383 | passed: 0 failed: 1 of 1 tests (11ms) 384 | 385 | 1 of 1 tests failed. 386 | 387 |  388 | **************************************************************************************************************** 389 | tapArc 390 | **************************************************************************************************************** 391 |  392 | 393 |  medium object diff 394 |   ✖ 1) should be deeply equivalent 395 |    { 396 |    ··"turn": { 397 |    ····"count": 1, 398 |    ····"player": "within(1, 4)", 399 |    ····"phase": "draw" 400 |    ··}, 401 |    ··"drawPile": "length(42)", 402 |    ··"discardPile": [], 403 |    ··"teams": [ 404 |    ····{ 405 |    ······"players": [ 406 |    ········{ 407 |    ··········"name": "Player 1", 408 |    ··········"cards": "length(11)", 409 |    ··········"lastDraw": null 410 |    ········}, 411 |    ········{ 412 |    ··········"name": "Player 2", 413 |    ··········"cards": "length(11)", 414 |    ··········"lastDraw": null 415 |    ········} 416 |    ······], 417 |    ······"reservePile": "length(11)", 418 |    ······"board": [] 419 |    ····}, 420 |    ····{ 421 |    ······"players": [ 422 |    ········{ 423 |    ··········"name": "Player 3", 424 |    ··········"cards": "length(11)", 425 |    ··········"lastDraw": null 426 |    ········}, 427 |    ········{ 428 |    ··········"name": "Player 4", 429 |    ··········"cards": "length(11)", 430 |    ··········"lastDraw": null 431 |    ········} 432 |    ······], 433 |    ······"reservePile": "length(11)", 434 |    ······"board": [] 435 |    ····} 436 |    ··] 437 |    } 438 |    At: Test. (/test/tapeTest.js:32:5) 439 |     440 | 441 |  Failed tests: There was 1 failure 442 | 443 |   ✖ 1) should be deeply equivalent 444 | 445 |  total: 1 446 |  failing: 1 447 |  34 ms 448 | 449 |  450 | **************************************************************************************************************** 451 | tapDifflet 452 | **************************************************************************************************************** 453 |  454 | 455 | medium object diff 456 | ⨯ should be deeply equivalent 457 |  458 | { turn: { count: 1, player: 'within(1, 4)', phase: 'draw' }, drawPile: 'length(42)', discardPile: [], teams: [ { players: [ { name: 'Player 1', cards: 'length(11)', lastDraw: null }, { name: 'Player 2', cards: 'length(11)', lastDraw: null } ], reservePile: 'length(11)', board: [] }, { players: [ { name: 'Player 3', cards: 'length(11)', lastDraw: null }, { name: 'Player 4', cards: 'length(11)', lastDraw: null } ], reservePile: 'length(11)', board: [] } ] } 459 | 460 | At: Test. (/home/ddv/dev/projects/comparing-testing-libraries/test/tapeTest.js:32:5) 461 | 462 | 1 failing 463 | 0 passing (17ms) 464 | 465 |  466 | **************************************************************************************************************** 467 | tapNirvana 468 | **************************************************************************************************************** 469 |  470 | 471 | medium object diff 472 | ✖ should be deeply equivalent 473 | ------------------------------- 474 | operator: deepEqual 475 | expected: { 476 | "turn": {"count": 1, "player": "within(1, 4)", "phase": "draw"}, 477 | "drawPile": "length(42)", 478 | "discardPile": [], 479 | "teams": [ 480 | { 481 | "players": [ 482 | {"name": "Player 1", "cards": "length(11)", "lastDraw": null}, 483 | {"name": "Player 2", "cards": "length(11)", "lastDraw": null} 484 | ], 485 | "reservePile": "length(11)", 486 | "board": [] 487 | }, 488 | { 489 | "players": [ 490 | {"name": "Player 3", "cards": "length(11)", "lastDraw": null}, 491 | {"name": "Player 4", "cards": "length(11)", "lastDraw": null} 492 | ], 493 | "reservePile": "length(11)", 494 | "board": [] 495 | } 496 | ] 497 | } 498 | diff: { 499 | teams: [ 500 | 1: { 501 | players: [ 502 | 0: { 503 | - lastDraw: null 504 | } 505 | 1: { 506 | - lastDraw: null 507 | } 508 | ] 509 | } 510 | ] 511 | } 512 | source: at Test. (/home/ddv/dev/projects/comparing-testing-libraries/test/tapeTest.js:32:5) 513 | at processImmediate (node:internal/timers:464:21) 514 |  515 | 516 | 517 | 518 | 519 | passed: 0, failed: 1 of 1 tests (11ms) 520 | -------------------------------------------------------------------------------- /out/results.md: -------------------------------------------------------------------------------- 1 | | Command | Mean [ms] | Min [ms] | Max [ms] | Relative | 2 | |:---|---:|---:|---:|---:| 3 | | `node test/special/notestTest.js` | 56.0 ± 2.0 | 50.3 | 60.2 | 3.54 ± 0.22 | 4 | | `node test/bestTest.js` | 57.4 ± 4.5 | 51.5 | 85.7 | 3.63 ± 0.34 | 5 | | `bun test ./test/bunTest.js` | 15.8 ± 0.8 | 13.7 | 17.7 | 1.00 | 6 | | `node test/tehanuTest.js` | 60.4 ± 3.6 | 56.0 | 77.3 | 3.82 ± 0.30 | 7 | | `node ./test/zoraTest.js` | 61.2 ± 2.0 | 56.7 | 65.8 | 3.87 ± 0.23 | 8 | | `xv test/special/xvTest.js` | 60.1 ± 4.1 | 55.2 | 76.6 | 3.80 ± 0.32 | 9 | | `node --test-reporter dot ./test/nativeTest.js` | 71.2 ± 3.8 | 66.6 | 91.1 | 4.50 ± 0.33 | 10 | | `node test/uvuTest.js` | 66.3 ± 2.2 | 61.8 | 72.0 | 4.19 ± 0.26 | 11 | | `tape test/tapeTest.js` | 134.8 ± 2.3 | 129.5 | 138.5 | 8.52 ± 0.46 | 12 | | `mocha -r chai/register-expect.js --inline-diffs --bail --ui=tdd --leaks --reporter min test/mochaTest.js` | 220.2 ± 5.8 | 214.3 | 234.0 | 13.93 ± 0.80 | 13 | | `lab test/labTest.js` | 259.5 ± 3.4 | 253.1 | 264.1 | 16.41 ± 0.86 | 14 | | `tap --disable-coverage ./test/tapTest.js` | 1982.1 ± 30.7 | 1947.5 | 2045.0 | 125.33 ± 6.68 | 15 | | `ava --serial --fail-fast test/avaTest.js` | 580.1 ± 21.1 | 556.0 | 631.4 | 36.68 ± 2.30 | 16 | | `vitest` | 654.4 ± 26.7 | 621.7 | 721.2 | 41.38 ± 2.70 | 17 | -------------------------------------------------------------------------------- /out/smallDiffErrors.ansi: -------------------------------------------------------------------------------- 1 |  2 | **************************************************************************************************************** 3 | jest 4 | **************************************************************************************************************** 5 |  6 | Determining test suites to run.... 7 |  ● insert saves the data to the database 8 | 9 | expect(received).toEqual(expected) // deep equality 10 | 11 | - Expected - 1 12 | + Received + 1 13 | 14 |  Array [ 15 |  Object { 16 |  "description": "average height", 17 |  "email": "john@test.com", 18 | - "name": "John1", 19 | + "name": "John", 20 |  }, 21 |  ] 22 |  23 |    9 | 24 |    10 | const expected = process.env.mode === 'equalError' ? [{ ...data, name: 'John1' }] : [data]; 25 |  > 11 | expect(actual).toEqual(expected); 26 |    | ^ 27 |    12 | await employee.removeAll();  28 |    13 | }); 29 |    14 | 30 |  31 |  at Object. (test/jestTest.js:11:20) 32 | 33 | 34 | 35 |  36 | **************************************************************************************************************** 37 | ava 38 | **************************************************************************************************************** 39 |  40 | 41 | ✖ insert saves the data to the database 42 | ─ 43 | 44 | insert saves the data to the database 45 | 46 | test/avaTest.js:11 47 | 48 | 10: const expected = process.env.mode === 'equalError' ? [{ ...data, name: 'John1' }] : [data]; 49 |  11: eq(actual, expected);  50 | 12: await employee.removeAll(); 51 | 52 | Difference: 53 | 54 | [ 55 | { 56 | description: 'average height', 57 | email: 'john@test.com', 58 | - name: 'John', 59 | + name: 'John1', 60 | }, 61 | ] 62 | 63 | › test/avaTest.js:11:5 64 | 65 | ─ 66 | 67 | 1 test failed 68 |  69 | **************************************************************************************************************** 70 | mocha 71 | **************************************************************************************************************** 72 |  73 | (node:24067) [DEP0148] DeprecationWarning: Use of deprecated folder mapping "./" in the "exports" field module resolution of the package at /home/ddv/dev/projects/comparing-testing-libraries/node_modules/chai/package.json imported from /home/ddv/dev/projects/comparing-testing-libraries/node_modules/mocha/lib/nodejs/esm-utils.js. 74 | Update this package.json to use a subpath pattern like "./*". 75 | (Use `node --trace-deprecation ...` to show where the warning was created) 76 |  77 |   0 passing (5ms) 78 |  1 failing 79 | 80 |  1) insert saves the data to the database: 81 | 82 | AssertionError: expected [ Array(1) ] to deeply equal [ Array(1) ] 83 | actual expected 84 | 85 | 1 | [ 86 | 2 | { 87 | 3 | "description": "average height" 88 | 4 | "email": "john@test.com" 89 | 5 | "name": "JohnJohn1" 90 | 6 | } 91 | 7 | ] 92 |  93 | at Context. (test/mochaTest.js:11:28) 94 |  95 | 96 | 97 |  98 | **************************************************************************************************************** 99 | lab 100 | **************************************************************************************************************** 101 |  102 | 103 | 104 | x 105 | 106 | Failed tests: 107 | 108 | 1) insert saves the data to the database: 109 | 110 | actual expected 111 | 112 | [ 113 | { 114 | description: 'average height', 115 | email: 'john@test.com', 116 | name: 'JohnJohn1' 117 | } 118 | ] 119 | 120 | Expected [Array(1)] to equal specified value: [Array(1)] 121 | 122 |  at /home/ddv/dev/projects/comparing-testing-libraries/test/labTest.js:13:23 123 | 124 | 125 | 1 of 1 tests failed 126 | Test duration: 5 ms 127 | Leaks: No issues 128 | 129 |  130 | **************************************************************************************************************** 131 | pta 132 | **************************************************************************************************************** 133 |  134 | 135 |  FAIL  insert saves the data to the database 136 | at: /home/ddv/dev/projects/comparing-testing-libraries/test/zoraTest.js:11:5 137 | 138 | should be equivalent 139 | [equal] diff in objects: 140 |  - actual   + expected  141 | 142 |  [  143 |  {  144 |  "description": "average height",  145 |  "email": "john@test.com",  146 |  -  "name": "John" 147 |  +  "name": "John1" 148 |  }  149 |  ]  150 | 151 | TOTAL: 1 152 |  PASS: 0  153 |  FAIL: 1  154 |  SKIP: 0  155 | 156 |  157 | **************************************************************************************************************** 158 | best 159 | **************************************************************************************************************** 160 |  161 | AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: 162 | + actual - expected 163 | 164 | [ 165 | { 166 | description: 'average height', 167 | email: 'john@test.com', 168 | + name: 'John' 169 | - name: 'John1' 170 | } 171 | ] 172 | at Object.fn (/home/ddv/dev/projects/comparing-testing-libraries/test/bestTest.js:11:5) 173 | at async Immediate.run (/home/ddv/dev/projects/comparing-testing-libraries/best/best.js:12:13) 174 |  175 | **************************************************************************************************************** 176 | tapDiff2 177 | **************************************************************************************************************** 178 |  179 | 180 | insert saves the data to the database 181 | ✖ should be deeply equivalent at Test. (/home/ddv/dev/projects/comparing-testing-libraries/test/tapeTest.js:11:5) 182 | [ 183 | { 184 | "description": "average height", 185 | "email": "john@test.com", 186 | "name": "JohnJohn1" 187 | } 188 | ] 189 | 190 | passed: 0 failed: 1 of 1 tests (10ms) 191 | 192 | 1 of 1 tests failed. 193 | 194 |  195 | **************************************************************************************************************** 196 | tapArc 197 | **************************************************************************************************************** 198 |  199 | 200 |  insert saves the data to the database 201 |   ✖ 1) should be deeply equivalent 202 |    [ 203 |    ··{ 204 |    ····"name": "John1", 205 |    ····"email": "john@test.com", 206 |    ····"description": "average height" 207 |    ··} 208 |    ] 209 |    At: Test. (/test/tapeTest.js:11:5) 210 |     211 | 212 |  Failed tests: There was 1 failure 213 | 214 |   ✖ 1) should be deeply equivalent 215 | 216 |  total: 1 217 |  failing: 1 218 |  21 ms 219 | 220 |  221 | **************************************************************************************************************** 222 | tapDifflet 223 | **************************************************************************************************************** 224 |  225 | 226 | insert saves the data to the database 227 | ⨯ should be deeply equivalent 228 |  229 | [ { name: 'John1', email: 'john@test.com', description: 'average height' } ] 230 | 231 | At: Test. (/home/ddv/dev/projects/comparing-testing-libraries/test/tapeTest.js:11:5) 232 | 233 | 1 failing 234 | 0 passing (10ms) 235 | 236 |  237 | **************************************************************************************************************** 238 | tapNirvana 239 | **************************************************************************************************************** 240 |  241 | 242 | insert saves the data to the database 243 | ✖ should be deeply equivalent 244 | ------------------------------- 245 | operator: deepEqual 246 | expected: [{"name": "John1", "email": "john@test.com", "description": "average height"}] 247 | diff: [ 248 | 0: { 249 | name: "John1" => "John" 250 | } 251 | ] 252 | source: at Test. (/home/ddv/dev/projects/comparing-testing-libraries/test/tapeTest.js:11:5) 253 | at processTicksAndRejections (node:internal/process/task_queues:96:5) 254 |  255 | 256 | 257 | 258 | 259 | passed: 0, failed: 1 of 1 tests (9ms) 260 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "SEE RUN.SH": "echo SEE RUN.SH" 4 | }, 5 | "type": "module", 6 | "dependencies": { 7 | "@hapi/code": "^9.0.3", 8 | "@hapi/lab": "^26.0.0", 9 | "ava": "^6.2.0", 10 | "baretest": "^2.0.0", 11 | "chai": "^5.2.0", 12 | "codedent": "^0.1.2", 13 | "jest": "^29.7.0", 14 | "jest-silent-reporter": "^0.6.0", 15 | "lodash": "^4.17.21", 16 | "mocha": "^11.1.0", 17 | "pta": "^1.3.0", 18 | "tap": "^21.1.0", 19 | "tape": "^5.9.0", 20 | "tehanu": "^1.0.1", 21 | "uvu": "^0.5.6", 22 | "vitest": "^3.0.6", 23 | "xv": "^2.1.1", 24 | "zora": "^6.0.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source $(dirname $0)/scripts/utils.sh 4 | source $(dirname $0)/scripts/aux.sh 5 | 6 | PATH=$PATH:node_modules/.bin 7 | 8 | ava="ava --serial --fail-fast test/avaTest.js" 9 | best="node test/bestTest.js" 10 | jest="node --experimental-vm-modules node_modules/jest/bin/jest.js --runInBand" # can't specify file here, experimental-vm-modules needed for ESM 11 | lab="lab test/labTest.js" 12 | mocha="mocha -r chai/register-expect.js --inline-diffs --bail --ui=tdd --leaks --reporter min test/mochaTest.js" 13 | tap="tap --disable-coverage --reporter=silent ./test/tapTest.js" 14 | tape="tape test/tapeTest.js" 15 | tehanu="node test/tehanuTest.js" 16 | uvu="node test/uvuTest.js" 17 | zora="node ./test/zoraTest.js" 18 | native="node --test-reporter dot ./test/nativeTest.js" 19 | vitest="vitest" #can't specify file here 20 | notest="node test/special/notestTest.js" 21 | xv="xv test/special/xvTest.js" 22 | bun="bun test ./test/bunTest.js" 23 | 24 | perf="hyperfine -N --warmup 5 --export-markdown out/results.md '$notest' '$best' '$bun' '$tehanu' '$zora' '$xv' '$native' '$uvu' '$tape' '$mocha' '$lab' '$tap' '$ava' '$vitest' '$jest'" 25 | genBaseTests=_genBaseTests 26 | 27 | 28 | # usage: ./run.sh LIBNAME 29 | echo $(purple "command") "${!1:-not found}"; eval ${!1} 30 | -------------------------------------------------------------------------------- /scripts/aux.sh: -------------------------------------------------------------------------------- 1 | # replaces test/* with simple tests 2 | _genBaseTests() { template=baseTemplate node generators/generate.js; } 3 | 4 | # replaces test/* with tests causing an error on an object of medium size 5 | _genMediumTests() { template=mediumDiffErrorTemplate node generators/generate.js; } 6 | 7 | # replaces test/* with tests causing an error on an object of large size 8 | _genLargeTests() { template=largeDiffErrorTemplate node generators/generate.js; } 9 | 10 | 11 | smallDiffErrorsReport() { _genBaseTests; _doReport $1 $2 "smallDiffErrors" "mode=equalError"; } 12 | mediumDiffErrorsReport() { _genMediumTests; _doReport $1 $2 "mediumDiffErrors" ""; } 13 | largeDiffErrorsReport() { _genLargeTests; _doReport $1 $2 "largeDiffErrors" ""; } 14 | 15 | # generates assertion error reports in out/*.ansi 16 | diffErrorsReport() { 17 | libs=(jest ava mocha pta best) 18 | tapLibs=(tapDiff2 tapArc tapDifflet tapNirvana) 19 | 20 | smallDiffErrorsReport libs tapLibs 21 | mediumDiffErrorsReport libs tapLibs 22 | largeDiffErrorsReport libs tapLibs 23 | } 24 | 25 | npmCompare="node_modules/npm-compare/cli.js angular vue > x.ansi" 26 | -------------------------------------------------------------------------------- /scripts/diffErrors.sh: -------------------------------------------------------------------------------- 1 | _doReport() { 2 | local -n libs1=$1 3 | local -n tapLibs1=$2 4 | outputName="out/${3}.ansi" 5 | command=$4 6 | 7 | rm -f $outputName 8 | for lib in "${libs1[@]}" 9 | do 10 | header "${lib}" >> $outputName 11 | script -q /dev/null -c "$command eval ${!lib}" >> $outputName 12 | done 13 | 14 | for lib in "${tapLibs1[@]}" 15 | do 16 | header "${lib}" >> $outputName 17 | script -q /dev/null -c "$command $tape | ${!lib}" >> $outputName 18 | done 19 | } -------------------------------------------------------------------------------- /scripts/utils.sh: -------------------------------------------------------------------------------- 1 | # COMMON COMMANDS 2 | # 3 | # echo -n does not append \n, usefull to have multiple echos appending to the same line 4 | # -e enable backslashes. Ex: \n will output a new line 5 | 6 | RED='\033[0;31m' 7 | GREEN='\033[0;32m' 8 | PURPLE='\033[0;35m' 9 | RESET='\033[0m' 10 | 11 | purple() { echo -e "${PURPLE}$1${RESET}" ; } 12 | red() { echo -e "${RED}$1${RESET}" ; } 13 | green() { echo -e "${GREEN}$1${RESET}" ; } 14 | 15 | # adds a new line if last line if not empty 16 | ensureNewline() { 17 | if [ "$(tail -c1 "$1"; echo x)" != $'\nx' ]; then 18 | echo "" >>"$1" 19 | fi 20 | } 21 | 22 | asHHMMSS() { 23 | echo "$(( ${1} / 3600 ))h $(( (${1} / 60) % 60 ))m $(( ${1} % 60 ))s" 24 | } 25 | 26 | # transforms seconds to format MMSS 27 | # Ex: $(asMMSS 600) => 28 | asMMSS() { 29 | echo "$(( ${1} / 60 ))m $(( ${1} % 60 ))s" 30 | } 31 | 32 | header () { 33 | echo -e "${PURPLE}" 34 | echo "****************************************************************************************************************" 35 | echo -e $1 36 | echo "****************************************************************************************************************" 37 | echo -e "${RESET}" 38 | } 39 | 40 | header2 () { 41 | echo "" 42 | purple "$1" 43 | } 44 | 45 | -------------------------------------------------------------------------------- /src/employee.cjs: -------------------------------------------------------------------------------- 1 | let db = []; 2 | 3 | module.exports = { 4 | 5 | async insert(data) { 6 | if (process.env.mode === 'exception') throw new Error('Testing'); 7 | db.push(data); 8 | }, 9 | 10 | async find() { 11 | return db; 12 | }, 13 | 14 | async removeAll() { 15 | db = []; 16 | }, 17 | } -------------------------------------------------------------------------------- /src/employee.js: -------------------------------------------------------------------------------- 1 | let db = []; 2 | 3 | export default { 4 | 5 | async insert(data) { 6 | if (process.env.mode === 'exception') throw new Error('Testing'); 7 | db.push(data); 8 | }, 9 | 10 | async find() { 11 | return db; 12 | }, 13 | 14 | async removeAll() { 15 | db = []; 16 | }, 17 | } 18 | -------------------------------------------------------------------------------- /test/avaTest.js: -------------------------------------------------------------------------------- 1 | import employee from '../src/employee.js'; 2 | import test from 'ava'; 3 | 4 | test('insert saves the data to the database', async ({ deepEqual: eq }) => { 5 | const data = { name: 'John', email: 'john@test.com', description: 'average height' }; 6 | await employee.insert(data); 7 | 8 | const actual = await employee.find(); 9 | 10 | const expected = process.env.mode === 'equalError' ? [{ ...data, name: 'John1' }] : [data]; 11 | eq(actual, expected); 12 | await employee.removeAll(); 13 | }); 14 | 15 | -------------------------------------------------------------------------------- /test/baretestTest.js: -------------------------------------------------------------------------------- 1 | import employee from '../src/employee.js'; 2 | 3 | import baretest from 'baretest'; 4 | import { deepStrictEqual as eq } from 'node:assert'; 5 | const test = baretest('My app'); 6 | 7 | 8 | test('insert saves the data to the database', async () => { 9 | const data = { name: 'John', email: 'john@test.com', description: 'average height' }; 10 | await employee.insert(data); 11 | 12 | const actual = await employee.find(); 13 | 14 | const expected = process.env.mode === 'equalError' ? [{ ...data, name: 'John1' }] : [data]; 15 | eq(actual, expected); 16 | await employee.removeAll(); 17 | }); 18 | 19 | await test.run(); -------------------------------------------------------------------------------- /test/bestTest.js: -------------------------------------------------------------------------------- 1 | import employee from '../src/employee.js'; 2 | import { test, eq } from '../best/best.js'; 3 | 4 | test('insert saves the data to the database', async () => { 5 | const data = { name: 'John', email: 'john@test.com', description: 'average height' }; 6 | await employee.insert(data); 7 | 8 | const actual = await employee.find(); 9 | 10 | const expected = process.env.mode === 'equalError' ? [{ ...data, name: 'John1' }] : [data]; 11 | eq(actual, expected); 12 | await employee.removeAll(); 13 | }); 14 | 15 | -------------------------------------------------------------------------------- /test/bunTest.js: -------------------------------------------------------------------------------- 1 | import employee from '../src/employee.js'; 2 | import { test, expect } from 'bun:test'; 3 | 4 | test('insert saves the data to the database', async () => { 5 | const data = { name: 'John', email: 'john@test.com', description: 'average height' }; 6 | await employee.insert(data); 7 | 8 | const actual = await employee.find(); 9 | 10 | const expected = process.env.mode === 'equalError' ? [{ ...data, name: 'John1' }] : [data]; 11 | expect(actual).toEqual(expected); 12 | await employee.removeAll(); 13 | }); 14 | 15 | -------------------------------------------------------------------------------- /test/jestTest.js: -------------------------------------------------------------------------------- 1 | import employee from '../src/employee.js'; 2 | 3 | test('insert saves the data to the database', async () => { 4 | const data = { name: 'John', email: 'john@test.com', description: 'average height' }; 5 | await employee.insert(data); 6 | 7 | const actual = await employee.find(); 8 | 9 | const expected = process.env.mode === 'equalError' ? [{ ...data, name: 'John1' }] : [data]; 10 | expect(actual).toEqual(expected); 11 | await employee.removeAll(); 12 | }); 13 | 14 | -------------------------------------------------------------------------------- /test/labTest.js: -------------------------------------------------------------------------------- 1 | import employee from '../src/employee.js'; 2 | 3 | import Code from '@hapi/code'; 4 | import Lab from '@hapi/lab'; 5 | import { expect } from '@hapi/code'; 6 | export const lab = Lab.script(); 7 | const { test } = lab; 8 | 9 | 10 | test('insert saves the data to the database', async () => { 11 | const data = { name: 'John', email: 'john@test.com', description: 'average height' }; 12 | await employee.insert(data); 13 | 14 | const actual = await employee.find(); 15 | 16 | const expected = process.env.mode === 'equalError' ? [{ ...data, name: 'John1' }] : [data]; 17 | expect(actual).to.equal(expected); 18 | await employee.removeAll(); 19 | }); 20 | 21 | Code.settings.truncateMessages = true; -------------------------------------------------------------------------------- /test/mochaTest.js: -------------------------------------------------------------------------------- 1 | import employee from '../src/employee.js'; 2 | 3 | 4 | test('insert saves the data to the database', async () => { 5 | const data = { name: 'John', email: 'john@test.com', description: 'average height' }; 6 | await employee.insert(data); 7 | 8 | const actual = await employee.find(); 9 | 10 | const expected = process.env.mode === 'equalError' ? [{ ...data, name: 'John1' }] : [data]; 11 | expect(actual).to.deep.equal(expected); 12 | await employee.removeAll(); 13 | }); 14 | 15 | -------------------------------------------------------------------------------- /test/nativeTest.js: -------------------------------------------------------------------------------- 1 | import employee from '../src/employee.js'; 2 | 3 | import { test } from 'node:test'; 4 | import { deepStrictEqual as eq } from 'node:assert'; 5 | 6 | test('insert saves the data to the database', async () => { 7 | const data = { name: 'John', email: 'john@test.com', description: 'average height' }; 8 | await employee.insert(data); 9 | 10 | const actual = await employee.find(); 11 | 12 | const expected = process.env.mode === 'equalError' ? [{ ...data, name: 'John1' }] : [data]; 13 | eq(actual, expected); 14 | await employee.removeAll(); 15 | }); 16 | 17 | -------------------------------------------------------------------------------- /test/special/notestTest.js: -------------------------------------------------------------------------------- 1 | import employee from '../../src/employee.js'; 2 | import { deepStrictEqual as eq } from 'node:assert'; 3 | 4 | const data = { name: 'John', email: 'john@test.com', description: 'average height' }; 5 | await employee.insert(data); 6 | 7 | const actual = await employee.find(); 8 | 9 | const expected = process.env.mode === 'equalError' ? [{ ...data, name: 'John1' }] : [data]; 10 | // if (!eq(actual, expected)) throw new Error(); 11 | eq(actual, expected); 12 | await employee.removeAll(); 13 | -------------------------------------------------------------------------------- /test/special/xvTest.js: -------------------------------------------------------------------------------- 1 | import employee from '../../src/employee.js'; 2 | import { deepStrictEqual as eq } from 'node:assert'; 3 | 4 | export async function testInsertSavesTheDataToTheDatabase() { 5 | const data = { name: 'John', email: 'john@test.com', description: 'average height' }; 6 | await employee.insert(data); 7 | 8 | const actual = await employee.find(); 9 | 10 | const expected = process.env.mode === 'equalError' ? [{ ...data, name: 'John1' }] : [data]; 11 | eq(actual, expected); 12 | await employee.removeAll(); 13 | }; 14 | 15 | // export function testAdd() { 16 | // assert.equal(1 + 3, 3) 17 | // } 18 | -------------------------------------------------------------------------------- /test/tapTest.js: -------------------------------------------------------------------------------- 1 | import employee from '../src/employee.js'; 2 | import { test } from 'tap'; 3 | 4 | test('insert saves the data to the database', async ({ same: eq, end }) => { 5 | const data = { name: 'John', email: 'john@test.com', description: 'average height' }; 6 | await employee.insert(data); 7 | 8 | const actual = await employee.find(); 9 | 10 | const expected = process.env.mode === 'equalError' ? [{ ...data, name: 'John1' }] : [data]; 11 | eq(actual, expected); 12 | await employee.removeAll(); 13 | end(); 14 | }); 15 | 16 | -------------------------------------------------------------------------------- /test/tapeTest.js: -------------------------------------------------------------------------------- 1 | import employee from '../src/employee.js'; 2 | import { test } from 'tape'; 3 | 4 | test('insert saves the data to the database', async ({ same: eq, end }) => { 5 | const data = { name: 'John', email: 'john@test.com', description: 'average height' }; 6 | await employee.insert(data); 7 | 8 | const actual = await employee.find(); 9 | 10 | const expected = process.env.mode === 'equalError' ? [{ ...data, name: 'John1' }] : [data]; 11 | eq(actual, expected); 12 | await employee.removeAll(); 13 | end(); 14 | }); 15 | 16 | -------------------------------------------------------------------------------- /test/tehanuTest.js: -------------------------------------------------------------------------------- 1 | import employee from '../src/employee.js'; 2 | 3 | import tehanu from 'tehanu'; 4 | const test = tehanu(''); 5 | import { deepStrictEqual as eq } from 'node:assert'; 6 | 7 | 8 | test('insert saves the data to the database', async () => { 9 | const data = { name: 'John', email: 'john@test.com', description: 'average height' }; 10 | await employee.insert(data); 11 | 12 | const actual = await employee.find(); 13 | 14 | const expected = process.env.mode === 'equalError' ? [{ ...data, name: 'John1' }] : [data]; 15 | eq(actual, expected); 16 | await employee.removeAll(); 17 | }); 18 | 19 | -------------------------------------------------------------------------------- /test/uvuTest.js: -------------------------------------------------------------------------------- 1 | import employee from '../src/employee.js'; 2 | 3 | import { suite } from 'uvu'; 4 | import { equal as eq } from 'uvu/assert'; 5 | const test = suite('employee'); 6 | 7 | 8 | test('insert saves the data to the database', async () => { 9 | const data = { name: 'John', email: 'john@test.com', description: 'average height' }; 10 | await employee.insert(data); 11 | 12 | const actual = await employee.find(); 13 | 14 | const expected = process.env.mode === 'equalError' ? [{ ...data, name: 'John1' }] : [data]; 15 | eq(actual, expected); 16 | await employee.removeAll(); 17 | }); 18 | 19 | await test.run(); -------------------------------------------------------------------------------- /test/vitestTest.js: -------------------------------------------------------------------------------- 1 | import employee from '../src/employee.js'; 2 | import { expect, it, describe, test } from 'vitest'; 3 | 4 | test('insert saves the data to the database', async () => { 5 | const data = { name: 'John', email: 'john@test.com', description: 'average height' }; 6 | await employee.insert(data); 7 | 8 | const actual = await employee.find(); 9 | 10 | const expected = process.env.mode === 'equalError' ? [{ ...data, name: 'John1' }] : [data]; 11 | expect(actual).toEqual(expected); 12 | await employee.removeAll(); 13 | }); 14 | 15 | -------------------------------------------------------------------------------- /test/zoraTest.js: -------------------------------------------------------------------------------- 1 | import employee from '../src/employee.js'; 2 | import { test } from 'zora'; 3 | 4 | test('insert saves the data to the database', async ({ equal: eq }) => { 5 | const data = { name: 'John', email: 'john@test.com', description: 'average height' }; 6 | await employee.insert(data); 7 | 8 | const actual = await employee.find(); 9 | 10 | const expected = process.env.mode === 'equalError' ? [{ ...data, name: 'John1' }] : [data]; 11 | eq(actual, expected); 12 | await employee.removeAll(); 13 | }); 14 | 15 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | isolate: false, 6 | fileParallelism: false, 7 | include: ['test/vitestTest.js'], 8 | watch: false, 9 | } 10 | }); 11 | --------------------------------------------------------------------------------