Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
node:test will replace Jest?
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
jiko21
May 09, 2023
130
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
node:test will replace Jest?
関西Node学園 10時限目の登壇資料です。
jiko21
May 09, 2023
More Decks by jiko21
See All by jiko21
型情報を手繰り寄せる技術~TypeScript Compiler APIによる型解析実践~
jiko21
0
1.2k
Creating a Next.js-style Framework with Bun and Hono
jiko21
0
200
Array Grouping will soon be arriving at TypeScript
jiko21
0
190
Copying Array Methods arrived at TypeScript
jiko21
1
1k
SSRで動的に OGP画像を生成したい! 〜Cloudflare Workersから@vercel/og移行編〜
jiko21
0
180
どこでも動かすために… TypeScriptでライブラリ開発の すゝめ
jiko21
2
490
NestJS a progressive web framework
jiko21
3
2.3k
レガシーなフロントエンドをリプレイスする
jiko21
5
1.6k
Deep Dive Into Vue Composition API
jiko21
0
3.3k
Featured
See All Featured
GraphQLとの向き合い方2022年版
quramy
50
15k
Evolving SEO for Evolving Search Engines
ryanjones
0
360
Six Lessons from altMBA
skipperchong
29
4.6k
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
2
440
Automating Front-end Workflow
addyosmani
1370
210k
Rebuilding a faster, lazier Slack
samanthasiow
85
9.7k
Money Talks: Using Revenue to Get Sh*t Done
nikkihalliwell
0
560
Claude Code のすすめ
schroneko
67
230k
Facilitating Awesome Meetings
lara
57
7.2k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
520
What's in a price? How to price your products and services
michaelherold
247
13k
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
2
6.4k
Transcript
node:test will replace Jest? ؔNodeֶԂ 10࣌ݶ @jiko21
About jiko21… Name: Daiki Kojima (jiko21) Multistack Engineer Love: Guitar,
TypeScript @jiko21 @jiko_21
node:test
nodeʹtest runnerੜ͑ͯ·͢ʂ • ग़ͨͷv18.0.0͔Β(ҰԠv16.17.0)ͰՃ͞Εͨ • ެࣜͰTest runnerͱ͍͏໊લ • ·ͩ·ͩStability: 1(Experimental)
• ͚ͩͬͨͲv20Ͱstableʹ • ొஃωλͷૉৼΓͯ͠ΔλΠϛϯάͰv20ग़ͯ͠·ͬͨ
jestͱൺֱͯ͠ΈΔϙΠϯτ • جຊతͳॻ͖ํɾ࣮ߦํ๏ͳͲ • describe, test, it… • beforeEach, afterEach…
• mock • context • coverage·ΘΓ • ύϑΥʔϚϯε
جຊతͳॻ͖ํ
γϯϓϧͳςετίʔυ 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ͯ͘͠ΕΔ
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') }); });
࣮ߦͷํ • ҎԼίϚϯυͰ࣮ߦՄೳ • ϑΝΠϧ໊Λࢦఆͯ͠ͷ࣮ߦՄೳ
ϑΝΠϧ໊Λࢦఆ͠ͳ͔ͬͨ߹ • ҎԼͷϧʔϧʹै࣮ͬͯߦ͞ΕΔ 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ࣗಈͰ࣮ߦ
࣮ߦ݁Ռ
࣮ߦ݁Ռ ޭɾࣦഊͱ࣮ߦ͕࣌ؒΘ͔Δ BTTFSU͕͍͍ײ͡ʹΤϥʔग़ͯ͘͠ΕΔ ͷͰΘ͔Γ͍͢
࣮ߦ݁Ռ ςεταϚϦʔͱ͔ग़ͯ͘͠ΕΔ
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); }); });
एׯҧ͏͚Ͳ΄΅ಉ͡ • beforeEachɺafterEachjestͱมΘΒͣ • beforeAllͱafterAll͚ͩAll͕ফ͑ͯbeforeɺafterʹ • v18·ͰdescribeͷԼʹit͕ͳ͍ͱಈ͔ͳ͍(testͩͱಈ͔ͳ͔ͬͨ) • v20Ͱ࣏༷ͬͨ
mock͍ͨ͠ʂ • ϩϯυϯֶ(ͳΔ͚ͩ୯ମͰςετ͢ΔͨΊʹϞοΫ͢Δ)͍͍ͷ ͔ʁٞ͋Δͱࢥ͏͕ɺՄೳɻ • ҎԼํ๏ͰmockՄೳ • import͍ͯ͠ΔobjectࣗମΛmock͢Δύλʔϯ • spy
functionΛͬͯmock͢Δํ๏
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ͷݺΕͨճ֬ೝՄೳ
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ͷҾͷݕূՄೳ
context
node:testʹcontext͕ଘࡏͯ͠·͢ʂ • test runnerͱΓऔΓ͢ΔͨΊʹɺtest()ͱ͔ʹTestContext͕ ͞Ε͍ͯΔ • GoΈ͍ͨͳײ͡ͰContext͕͑Δ
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); }); });
contextͷҙ • t.beforeଘࡏ͠ͳ͍ • test suiteͰॱ൪ʹ࣮ߦ͞ΕΔ͔Βͦ͜Ͱ୲อͰ͖Δ • t.testpromiseΛฦ͢ • await͠ͳ͍ͱtest͕ઌʹऴΘͬͯΤϥʔʹ
• ςετέʔεͷΧϯτ͕บ͋Γ • #{rootͷςετ}+#{t.testͷݸ} • લϖʔδͩͱ̏ςετέʔε • contextͷςετ͕མͪΔͱςετམͪͨ͜ͱʹͳΔ
coverage·ΘΓ
coverage·ΘΓ • ·ͩStability 1(Experimental) • —experimental-test-coverageϑϥάΛ ͚ͯ͋͛Δͱऩूͯ͘͠ΕΔ • ྲྀੴʹjest΄ͲϦονͳݟͨͰͳ͍
• coverageؚΊςετϨϙʔτΛΧελϜ Մೳ
ςετϨϙʔτͷΧελϚΠζ 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;
ύϑΥʔϚϯε
ςετπʔϧม͑ΔͨΊͷಈػ • ͍͍͔͢ʁ • ඇਪ(or ϝϯςऴྃ)ʹͳ͍ͬͯͳ͍͔ʁ • ͍͔ʁ • CIͷ࣮ߦ͕࣌ؒݮΔͱઅʹͳΔ
• ։ൃऀܦݧతʹخ͍͠
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
ςετର // 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;
mockͨ͠߹
mock͠ͳ͍߹
node:testͷ΄͏͕ૣͦ͏ • ͍͢͝γϯϓϧͳྫ͚ͩͲɺ2ഒ΄Ͳ͍ • ͋Μ·Γmock͋Γͳ͠ʹӨڹड͚ͯͳ͍
࣮ϓϩμΫτʹೖͰ͖Δ͔ʁ
ҎԼͷΑ͏ͳϓϩδΣΫτͰݕূ • TypeScriptͰ࡞ͬͨΦϨΦϨOSS • https://github.com/jiko21/ fl av-md • ts-jestͰςετΛ࣮ߦ͍ͯ͠Δ
Ͳ͏ͬͯಈ͔͔͢ʁ • jestͳΒts-jest͕͋Δ͚Ͳɺnode:testʹͦΜͳศརͳͷ (·ͩ)ͳ͍ • ҎԼͷํ๏Λߟ͑ͯΈͨ • ts-nodeͰ࣮ߦ •
શͯtsͰ͔͍ͯtranspileɺnodeͰී௨ʹ࣮ߦ • ςετରΛbuild→ςετϑΝΠϧΛjs
ts-nodeͰ࣮ߦ • ts-nodeͰtranspile࣮ͭͭ͠ߦ
શͯtsͰ͔͍ͯtranspileɺnodeͰී௨ʹ࣮ߦ • tscͰtest fi leΛtranspileɻtranspileͯ͠ੜ͞ΕͨjsΛnodeͰී௨ ʹ࣮ߦ
ςετରΛbuild→ςετϑΝΠϧΛjs • tscͰtest fi leΛtranspileɻtranspileͯ͠ੜ͞ΕͨjsΛnodeͰී௨ ʹ࣮ߦ
Ϗϧυ࣌ؒΛߟྀͯ͠ൺֱͨ݁͠Ռ • jestͳΒts-jest͕͋Δ͚Ͳɺnode:testʹͦΜͳศརͳͷ (·ͩ)ͳ͍ • ҎԼͷํ๏Λߟ͑ͯΈͨ • ts-nodeͰ࣮ߦ •
શͯtsͰ͔͍ͯtranspileɺnodeͰී௨ʹ࣮ߦ • ςετରΛbuild→ςετϑΝΠϧΛjs
࣮ߦ݁Ռ(Ϗϧυൈ͖) • node:testࣗମͷ ͍ • transpile͢Δରม͑ͯ ͦΜͳʹมΘΒͳ͍ •
ts-jest෦తʹtsͷbuild ͢ΔͷͰྲྀੴʹෆެฏ
࣮ߦ݁Ռ(ϏϧυؚΉ) • Ϗϧυ࣌ؒΛೖΕͯ͠·͏ͱ ରͯ͘͠ͳ͍ࣄ͕Θ͔Δ (ଟ͜Ε͕ެฏͳධՁ)
·ͱΊ • node:test͕v20ͰΑ͏͘stableʹͳͬͨʂ • Ұػೳ·ͩexperimental • ҰԠjestΑΓ͍ʂ • ϓϩδΣΫτͰ͑Δ͔ɺͱݴΘΕΔͱ·ͩݫ͍͔͠
ࢀߟ • https://nodejs.org/api/test.html