Upgrade to Pro — share decks privately, control downloads, hide ads and more …

node:test will replace Jest?

jiko21
May 09, 2023
30

node:test will replace Jest?

関西Node学園 10時限目の登壇資料です。

jiko21

May 09, 2023
Tweet

Transcript

  1. node:test


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

    View Slide

  2. About jiko21…
    Name: Daiki Kojima (jiko21)


    Multistack Engineer


    Love: Guitar, TypeScript
    @jiko21
    @jiko_21

    View Slide

  3. node:test

    View Slide

  4. nodeʹ΋test runnerੜ͑ͯ·͢ʂ
    • ग़ͨͷ͸v18.0.0͔Β(ҰԠv16.17.0)Ͱ΋௥Ճ͞Εͨ


    • ެࣜͰ͸Test runnerͱ͍͏໊લ


    • ·ͩ·ͩStability: 1(Experimental)


    • ͚ͩͬͨͲv20Ͱstableʹ


    • ొஃωλͷૉৼΓͯ͠ΔλΠϛϯάͰv20ग़ͯ͠·ͬͨ

    View Slide

  5. jestͱൺֱͯ͠ΈΔϙΠϯτ
    • جຊతͳॻ͖ํɾ࣮ߦํ๏ͳͲ


    • describe, test, it…


    • beforeEach, afterEach…


    • mock


    • context


    • coverage·ΘΓ


    • ύϑΥʔϚϯε౳

    View Slide

  6. جຊతͳॻ͖ํ

    View Slide

  7. γϯϓϧͳςετίʔυ
    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ͯ͘͠ΕΔ

    View Slide

  8. 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')


    });


    });


    View Slide

  9. ࣮ߦͷ࢓ํ
    • ҎԼίϚϯυͰ࣮ߦՄೳ


    • ϑΝΠϧ໊Λࢦఆͯ͠ͷ࣮ߦ΋Մೳ

    View Slide

  10. ϑΝΠϧ໊Λࢦఆ͠ͳ͔ͬͨ৔߹
    • ҎԼͷϧʔϧʹै࣮ͬͯߦ͞ΕΔ


    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΋ࣗಈͰ࣮ߦ

    View Slide

  11. ࣮ߦ݁Ռ

    View Slide

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

    View Slide

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

    View Slide

  14. 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);


    });


    });

    View Slide

  15. एׯҧ͏͚Ͳ΄΅ಉ͡
    • beforeEachɺafterEach͸jestͱมΘΒͣ


    • beforeAllͱafterAll͚ͩ͸All͕ফ͑ͯbeforeɺafterʹ


    • v18·ͰdescribeͷԼʹit͕ͳ͍ͱಈ͔ͳ͍(testͩͱಈ͔ͳ͔ͬͨ)


    • v20Ͱ࣏ͬͨ໛༷

    View Slide

  16. mock͍ͨ͠ʂ
    • ϩϯυϯֶ೿(ͳΔ͚ͩ୯ମͰςετ͢ΔͨΊʹϞοΫ͢Δ)͸͍͍ͷ
    ͔ʁٞ࿦͸͋Δͱ͸ࢥ͏͕ɺՄೳɻ


    • ҎԼํ๏ͰmockՄೳ


    • import͍ͯ͠ΔobjectࣗମΛmock͢Δύλʔϯ


    • spy functionΛ࢖ͬͯmock͢Δํ๏

    View Slide

  17. 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ͷݺ͹Εͨճ਺΋֬ೝՄೳ

    View Slide

  18. 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ͷҾ਺ͷݕূ΋Մೳ

    View Slide

  19. context

    View Slide

  20. node:testʹ͸context͕ଘࡏͯ͠·͢ʂ
    • test runnerͱ΍ΓऔΓ͢ΔͨΊʹɺtest()ͱ͔ʹ͸TestContext͕౉
    ͞Ε͍ͯΔ


    • GoΈ͍ͨͳײ͡ͰContext͕࢖͑Δ

    View Slide

  21. 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);


    });


    });


    View Slide

  22. contextͷ஫ҙ఺
    • t.before͸ଘࡏ͠ͳ͍


    • test suite಺Ͱॱ൪ʹ࣮ߦ͞ΕΔ͔Βͦ͜Ͱ୲อͰ͖Δ


    • t.test͸promiseΛฦ͢


    • await͠ͳ͍ͱ਌test͕ઌʹऴΘͬͯΤϥʔʹ


    • ςετέʔε਺ͷΧ΢ϯτ͕บ͋Γ


    • #{rootͷςετ}+#{t.testͷݸ਺}


    • લϖʔδͩͱ̏ςετέʔε


    • contextͷςετ͕མͪΔͱ਌ςετ΋མͪͨ͜ͱʹͳΔ

    View Slide

  23. coverage·ΘΓ

    View Slide

  24. coverage·ΘΓ
    • ·ͩStability 1(Experimental)


    • —experimental-test-coverageϑϥάΛ

    ෇͚ͯ͋͛Δͱऩूͯ͘͠ΕΔ


    • ྲྀੴʹjest΄ͲϦονͳݟͨ໨Ͱ͸ͳ͍


    • coverageؚΊςετϨϙʔτΛΧελϜ

    Մೳ

    View Slide

  25. ςετϨϙʔτͷΧελϚΠζ
    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;

    View Slide

  26. ύϑΥʔϚϯε౳

    View Slide

  27. ςετπʔϧ౳ม͑ΔͨΊͷಈػ
    • ࢖͍΍͍͔͢ʁ


    • ඇਪ঑(or ϝϯςऴྃ)ʹͳ͍ͬͯͳ͍͔ʁ


    • ଎͍͔ʁ


    • CIͷ࣮ߦ͕࣌ؒݮΔͱઅ໿ʹͳΔ


    • ։ൃऀܦݧతʹ΋خ͍͠

    View Slide

  28. 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

    View Slide

  29. ςετର৅
    // 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;

    View Slide

  30. mockͨ͠৔߹

    View Slide

  31. mock͠ͳ͍৔߹

    View Slide

  32. node:testͷ΄͏͕ૣͦ͏
    • ͍͢͝γϯϓϧͳྫ͚ͩͲɺ2ഒ΄Ͳ͸΍͍


    • ͋Μ·Γmock͋Γͳ͠ʹӨڹड͚ͯͳ͍

    View Slide

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

    View Slide

  34. ҎԼͷΑ͏ͳϓϩδΣΫτͰݕূ
    • TypeScriptͰ࡞ͬͨΦϨΦϨOSS


    • https://github.com/jiko21/
    fl
    av-md


    • ts-jestͰςετΛ࣮ߦ͍ͯ͠Δ

    View Slide

  35. Ͳ͏΍ͬͯಈ͔͔͢ʁ
    • jestͳΒts-jest͕͋Δ͚Ͳɺnode:testʹ͸ͦΜͳศརͳ΋ͷ͸

    (·ͩ)ͳ͍


    • ҎԼͷํ๏Λߟ͑ͯΈͨ


    • ts-nodeͰ࣮ߦ


    • શͯtsͰ͔͍ͯtranspileɺnodeͰී௨ʹ࣮ߦ


    • ςετର৅Λbuild→ςετϑΝΠϧΛjs

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  39. Ϗϧυ࣌ؒΛߟྀͯ͠ൺֱͨ݁͠Ռ
    • jestͳΒts-jest͕͋Δ͚Ͳɺnode:testʹ͸ͦΜͳศརͳ΋ͷ͸

    (·ͩ)ͳ͍


    • ҎԼͷํ๏Λߟ͑ͯΈͨ


    • ts-nodeͰ࣮ߦ


    • શͯtsͰ͔͍ͯtranspileɺnodeͰී௨ʹ࣮ߦ


    • ςετର৅Λbuild→ςετϑΝΠϧΛjs

    View Slide

  40. ࣮ߦ݁Ռ(Ϗϧυൈ͖)
    • node:testࣗମͷ଎౓͸

    ଎͍


    • transpile͢Δର৅ม͑ͯ΋

    ͦΜͳʹมΘΒͳ͍


    • ts-jest͸಺෦తʹtsͷbuild

    ͢ΔͷͰྲྀੴʹෆެฏ

    View Slide

  41. ࣮ߦ݁Ռ(ϏϧυؚΉ)
    • Ϗϧυ࣌ؒΛೖΕͯ͠·͏ͱ

    ରͯ͠଎͘ͳ͍ࣄ͕Θ͔Δ

    (ଟ෼͜Ε͕ެฏͳධՁ)

    View Slide

  42. ·ͱΊ
    • node:test͕v20ͰΑ͏΍͘stableʹͳͬͨʂ


    • Ұ෼ػೳ͸·ͩexperimental


    • ҰԠjestΑΓ͸଎͍ʂ


    • ϓϩδΣΫτͰ࢖͑Δ͔ɺͱݴΘΕΔͱ·ͩݫ͍͔͠΋

    View Slide

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

    View Slide