Slide 1

Slide 1 text

node:test will replace Jest? ؔ੢NodeֶԂ 10࣌ݶ໨ @jiko21

Slide 2

Slide 2 text

About jiko21… Name: Daiki Kojima (jiko21) Multistack Engineer Love: Guitar, TypeScript @jiko21 @jiko_21

Slide 3

Slide 3 text

node:test

Slide 4

Slide 4 text

nodeʹ΋test runnerੜ͑ͯ·͢ʂ • ग़ͨͷ͸v18.0.0͔Β(ҰԠv16.17.0)Ͱ΋௥Ճ͞Εͨ • ެࣜͰ͸Test runnerͱ͍͏໊લ • ·ͩ·ͩStability: 1(Experimental) • ͚ͩͬͨͲv20Ͱstableʹ • ొஃωλͷૉৼΓͯ͠ΔλΠϛϯάͰv20ग़ͯ͠·ͬͨ

Slide 5

Slide 5 text

jestͱൺֱͯ͠ΈΔϙΠϯτ • جຊతͳॻ͖ํɾ࣮ߦํ๏ͳͲ • describe, test, it… • beforeEach, afterEach… • mock • context • coverage·ΘΓ • ύϑΥʔϚϯε౳

Slide 6

Slide 6 text

جຊతͳॻ͖ํ

Slide 7

Slide 7 text

γϯϓϧͳςετίʔυ import {test} from 'node:test'; import assert from 'node:assert/strict'; test('͜Ε͸௨Δ', () => { assert.equal(2 + 2, 4); }); test('͜Ε͸མͪΔ', () => { assert.equal(2 + 2, 5); }); test('͜Ε΋མͪΔ', () => { throw new Error('sample error'); }); OPEFUFTUʹUFTUؔ܎ͷ΋ͷ͕ೖ͍ͬͯΔ BTTFSUܥͷϞδϡʔϧ΋ඪ४Ͱ͋Γ ίϨࣗମ͸OPEFWࠒ͔Βଘࡏ͍ͯ͠Δ ͪΌΜͱྫ֎ʹରͯ͠΋GBJMͯ͘͠ΕΔ

Slide 8

Slide 8 text

describeͱ͔ͰjestΈ͍ͨʹωετͯ͠ΈΔͱ… import {test, describe} from 'node:test'; import assert from 'node:assert/strict'; describe('sample test', {concurrency: false}, (t) => { test('passing test1', {concurrency: false}, (t) => { console.log('1') assert.equal(1, 2); console.log('OK') }); test('passing test2', {concurrency: false}, (t) => { console.log('2') assert.equal(1, 1); console.log('OK') }); });

Slide 9

Slide 9 text

࣮ߦͷ࢓ํ • ҎԼίϚϯυͰ࣮ߦՄೳ 
 
 • ϑΝΠϧ໊Λࢦఆͯ͠ͷ࣮ߦ΋Մೳ

Slide 10

Slide 10 text

ϑΝΠϧ໊Λࢦఆ͠ͳ͔ͬͨ৔߹ • ҎԼͷϧʔϧʹै࣮ͬͯߦ͞ΕΔ 1. cli౳ͰϑΝΠϧ໊Λࢦఆ͞Ε͍ͯͨΒͦΕΛ࣮ߦ 2. ↑ࢦఆ͞Εͯͳ͔ͬͨΒҎԼͷॱংͰ࠶ؼతʹϑΝΠϧΛ୳࣮ͯ͠ߦ 1. node_modules͸Ϣʔβʔ͕ࢦఆͯ͠ͳ͍ݶΓ࣮ߦ͠ͳ͍ 2. /testσΟϨΫτϦ͕͋Ε͹࠶ؼతʹ.js, .cjs, .mjsϑΝΠϧΛݟ͚࣮ͭͯߦ 3. ͦΕҎ֎ͷσΟϨΫτϦͰ͸ҎԼͷϧʔϧʹैͬͨίʔυ͕ςετίʔτͯ͠ѻΘΕΔ 1. ϑΝΠϧ໊͕testͰ׬શҰக 2. test-hogehoge.jsɺͷΑ͏ʹpre fi x͕test-ͷ΋ͷΛ࣮ߦ 3. ϑΝΠϧ໊͕.test,-test,_testͰऴΘΔ΋ͷ 4. CLI্Ͱ໌ࣔతʹࣔ͞Ε͍ͯΕ͹ɺ.node΍.json΋ࣗಈͰ࣮ߦ

Slide 11

Slide 11 text

࣮ߦ݁Ռ

Slide 12

Slide 12 text

࣮ߦ݁Ռ ੒ޭɾࣦഊͱ࣮ߦ͕࣌ؒΘ͔Δ BTTFSU͕͍͍ײ͡ʹΤϥʔग़ͯ͘͠ΕΔ ͷͰΘ͔Γ΍͍͢

Slide 13

Slide 13 text

࣮ߦ݁Ռ ςεταϚϦʔͱ͔΋ग़ͯ͘͠ΕΔ

Slide 14

Slide 14 text

beforeXXXɺafterXXXΈ͍ͨͳ͜ͱ͍ͨ͠ʂ import {describe, test, before, beforeEach, after, afterEach} from 'node:test'; import assert from 'node:assert/strict'; describe('test', async () => { before(() => { console.log('before'); }) beforeEach(() => { console.log('beforeEach'); }); afterEach(() => { console.log('afterEach'); }) after(() => { console.log('after'); }); test('͜Ε͸௨Δ', () => { console.log('test1'); assert.equal(2 + 2, 4); }); ɹtest('͜Ε͸མͪΔ', () => { console.log('test2'); assert.equal(2 + 2, 5); }); });

Slide 15

Slide 15 text

एׯҧ͏͚Ͳ΄΅ಉ͡ • beforeEachɺafterEach͸jestͱมΘΒͣ • beforeAllͱafterAll͚ͩ͸All͕ফ͑ͯbeforeɺafterʹ • v18·ͰdescribeͷԼʹit͕ͳ͍ͱಈ͔ͳ͍(testͩͱಈ͔ͳ͔ͬͨ) • v20Ͱ࣏ͬͨ໛༷

Slide 16

Slide 16 text

mock͍ͨ͠ʂ • ϩϯυϯֶ೿(ͳΔ͚ͩ୯ମͰςετ͢ΔͨΊʹϞοΫ͢Δ)͸͍͍ͷ ͔ʁٞ࿦͸͋Δͱ͸ࢥ͏͕ɺՄೳɻ • ҎԼํ๏ͰmockՄೳ • import͍ͯ͠ΔobjectࣗମΛmock͢Δύλʔϯ • spy functionΛ࢖ͬͯmock͢Δํ๏

Slide 17

Slide 17 text

import͍ͯ͠ΔobjectࣗମΛmock͢Δύλʔϯ // mocked.mjs const fn = (a, b) => { return a + b; }; export default { fn, } // mocked.mjs import mocked from './mocked.mjs'; export const someFunc1 = () => { return mocked.fn(1, 2); }; // test.mjs import {describe, test, mock} from 'node:test'; import assert from 'node:assert/strict'; import { someFunc1 } from './mock.mjs'; import { mockFn } from './mockfn.mjs'; import mocked from './mocked.mjs'; describe('mock sample', () => { test('mock already existing object method', () => { mock.method(mocked, 'fn', () => { return 334; }); assert.deepEqual(someFunc1(), 334); assert.strictEqual(mocked.fn.mock.calls.length, 1); }); }); ର৅ͷΦϒδΣΫτ಺ͷNFUIPEΛ 
 NPDL NPDLͷݺ͹Εͨճ਺΋֬ೝՄೳ

Slide 18

Slide 18 text

spy functionΛ࢖ͬͯmock͢Δํ๏ // mock.js export const someFunc2 = (ctx) => { console.log('execute'); ctx.logger('aaa'); console.log('end'); }; // test.js import {describe, test, mock} from 'node:test'; import assert from 'node:assert/strict'; import { someFunc2 } from './mock.mjs'; describe('mock sample', () => { test('use spy function', () => { const ctx = { logger: mock.fn((a) => { console.log(a); }), }; assert.strictEqual(ctx.logger.mock.calls.length, 0); someFunc2(ctx); assert.strictEqual(ctx.logger.mock.calls.length, 1); assert.deepEqual(ctx.logger.mock.calls[0].arguments, ['aaa']); }); }); ςετର৅ͷΦϒδΣΫτ಺ͷ 
 ϝιουΛNPDLʹม͑Δ NPDLͷҾ਺ͷݕূ΋Մೳ

Slide 19

Slide 19 text

context

Slide 20

Slide 20 text

node:testʹ͸context͕ଘࡏͯ͠·͢ʂ • test runnerͱ΍ΓऔΓ͢ΔͨΊʹɺtest()ͱ͔ʹ͸TestContext͕౉ ͞Ε͍ͯΔ • GoΈ͍ͨͳײ͡ͰContext͕࢖͑Δ

Slide 21

Slide 21 text

context(t)Λ࢖ͬͯςετΛॻ͍ͨύλʔϯ import {test} from 'node:test'; import assert from 'node:assert/strict'; test('test', async (t) => { t.beforeEach(() => { console.log('beforeEach'); }); t.afterEach(() => { console.log('afterEach'); }) t.after(() => { console.log('after'); }); await t.test('͜Ε͸௨Δ', () => { console.log('test1'); assert.equal(2 + 2, 4); }); await t.test('͜Ε͸མͪΔ', () => { console.log('test2'); assert.equal(2 + 2, 5); }); });

Slide 22

Slide 22 text

contextͷ஫ҙ఺ • t.before͸ଘࡏ͠ͳ͍ • test suite಺Ͱॱ൪ʹ࣮ߦ͞ΕΔ͔Βͦ͜Ͱ୲อͰ͖Δ • t.test͸promiseΛฦ͢ • await͠ͳ͍ͱ਌test͕ઌʹऴΘͬͯΤϥʔʹ • ςετέʔε਺ͷΧ΢ϯτ͕บ͋Γ • #{rootͷςετ}+#{t.testͷݸ਺} • લϖʔδͩͱ̏ςετέʔε • contextͷςετ͕མͪΔͱ਌ςετ΋མͪͨ͜ͱʹͳΔ

Slide 23

Slide 23 text

coverage·ΘΓ

Slide 24

Slide 24 text

coverage·ΘΓ • ·ͩStability 1(Experimental) • —experimental-test-coverageϑϥάΛ 
 ෇͚ͯ͋͛Δͱऩूͯ͘͠ΕΔ • ྲྀੴʹjest΄ͲϦονͳݟͨ໨Ͱ͸ͳ͍ • coverageؚΊςετϨϙʔτΛΧελϜ 
 Մೳ

Slide 25

Slide 25 text

ςετϨϙʔτͷΧελϚΠζ import { Transform } from 'node:stream'; const customReporter = new Transform({ writableObjectMode: true, transform(event, encoding, callback) { const nest = event.data.nesting; const nestSpace = ' '.repeat(nest * 2); switch (event.type) { case 'test:start': callback(null, `\x1b[39m${nestSpace}test ${event.data.name} started\n`); break; case 'test:pass': callback(null, `\x1b[32m${nestSpace}test ${event.data.name} passed\n`); break; case 'test:fail': callback(null, `\x1b[31m${nestSpace}test ${event.data.name} failed\n`); break; case 'test:plan': callback(null, `\x1b[39m${nestSpace}test plan\n`); break; case 'test:diagnostic': callback(null, `\x1b[39m${nestSpace}${event.data.message}\n`); break; case 'test:coverage': { const { totalLineCount } = event.data.summary.totals; callback(null, `\x1b[39m${nestSpace}total line count: ${totalLineCount}\n`); break; } } }, }); export default customReporter;

Slide 26

Slide 26 text

ύϑΥʔϚϯε౳

Slide 27

Slide 27 text

ςετπʔϧ౳ม͑ΔͨΊͷಈػ • ࢖͍΍͍͔͢ʁ • ඇਪ঑(or ϝϯςऴྃ)ʹͳ͍ͬͯͳ͍͔ʁ • ଎͍͔ʁ • CIͷ࣮ߦ͕࣌ؒݮΔͱઅ໿ʹͳΔ • ։ൃऀܦݧతʹ΋خ͍͠

Slide 28

Slide 28 text

jest vs node:testΛͯ͠ΈΔ • ҎԼͷΑ͏ͳύλʔϯͰjestͱnode:testͷ࣮ߦ଎౓Λൺֱ • mockΛߦ͏ύλʔϯ • https://github.com/jiko21/node-test-compare • mockΛߦΘͳ͍ύλʔϯ • https://github.com/jiko21/node-test-compare-without-mock

Slide 29

Slide 29 text

ςετର৅ // mocked.js const fn = (a, b) => { return a + b; }; module.exports = { fn, } // mock.js const mocked = require('./mocked'); const someFunc1 = () => { return mocked.fn(1, 2); }; module.exports.someFunc1 = someFunc1;

Slide 30

Slide 30 text

mockͨ͠৔߹

Slide 31

Slide 31 text

mock͠ͳ͍৔߹

Slide 32

Slide 32 text

node:testͷ΄͏͕ૣͦ͏ • ͍͢͝γϯϓϧͳྫ͚ͩͲɺ2ഒ΄Ͳ͸΍͍ • ͋Μ·Γmock͋Γͳ͠ʹӨڹड͚ͯͳ͍

Slide 33

Slide 33 text

࣮ϓϩμΫτʹ౤ೖͰ͖Δ͔ʁ

Slide 34

Slide 34 text

ҎԼͷΑ͏ͳϓϩδΣΫτͰݕূ • TypeScriptͰ࡞ͬͨΦϨΦϨOSS • https://github.com/jiko21/ fl av-md • ts-jestͰςετΛ࣮ߦ͍ͯ͠Δ

Slide 35

Slide 35 text

Ͳ͏΍ͬͯಈ͔͔͢ʁ • jestͳΒts-jest͕͋Δ͚Ͳɺnode:testʹ͸ͦΜͳศརͳ΋ͷ͸ 
 (·ͩ)ͳ͍ • ҎԼͷํ๏Λߟ͑ͯΈͨ • ts-nodeͰ࣮ߦ • શͯtsͰ͔͍ͯtranspileɺnodeͰී௨ʹ࣮ߦ • ςετର৅Λbuild→ςετϑΝΠϧΛjs

Slide 36

Slide 36 text

ts-nodeͰ࣮ߦ • ts-nodeͰtranspile࣮ͭͭ͠ߦ

Slide 37

Slide 37 text

શͯtsͰ͔͍ͯtranspileɺnodeͰී௨ʹ࣮ߦ • tscͰtest fi leΛtranspileɻtranspileͯ͠ੜ੒͞ΕͨjsΛnodeͰී௨ ʹ࣮ߦ

Slide 38

Slide 38 text

ςετର৅Λbuild→ςετϑΝΠϧΛjs • tscͰtest fi leΛtranspileɻtranspileͯ͠ੜ੒͞ΕͨjsΛnodeͰී௨ ʹ࣮ߦ

Slide 39

Slide 39 text

Ϗϧυ࣌ؒΛߟྀͯ͠ൺֱͨ݁͠Ռ • jestͳΒts-jest͕͋Δ͚Ͳɺnode:testʹ͸ͦΜͳศརͳ΋ͷ͸ 
 (·ͩ)ͳ͍ • ҎԼͷํ๏Λߟ͑ͯΈͨ • ts-nodeͰ࣮ߦ • શͯtsͰ͔͍ͯtranspileɺnodeͰී௨ʹ࣮ߦ • ςετର৅Λbuild→ςετϑΝΠϧΛjs

Slide 40

Slide 40 text

࣮ߦ݁Ռ(Ϗϧυൈ͖) • node:testࣗମͷ଎౓͸ 
 ଎͍ • transpile͢Δର৅ม͑ͯ΋ 
 ͦΜͳʹมΘΒͳ͍ • ts-jest͸಺෦తʹtsͷbuild 
 ͢ΔͷͰྲྀੴʹෆެฏ

Slide 41

Slide 41 text

࣮ߦ݁Ռ(ϏϧυؚΉ) • Ϗϧυ࣌ؒΛೖΕͯ͠·͏ͱ 
 ରͯ͠଎͘ͳ͍ࣄ͕Θ͔Δ 
 (ଟ෼͜Ε͕ެฏͳධՁ)

Slide 42

Slide 42 text

·ͱΊ • node:test͕v20ͰΑ͏΍͘stableʹͳͬͨʂ • Ұ෼ػೳ͸·ͩexperimental • ҰԠjestΑΓ͸଎͍ʂ • ϓϩδΣΫτͰ࢖͑Δ͔ɺͱݴΘΕΔͱ·ͩݫ͍͔͠΋

Slide 43

Slide 43 text

ࢀߟ • https://nodejs.org/api/test.html