最新。頭版

  • 本文永久連結: https://dwatow.github.io/2020/04-21-jest/jest-doc-4/
  • 讀 Jest Doc - 單元測試的結構、執行順序與名詞解釋

    1. 讀 Jest Doc - 單元測試的結構、執行順序與名詞解釋
      1. 單元測試的血脈
    2. Setup and Teardown
      1. Repeating Setup For Many Tests
      2. One-Time Setup
      3. Scoping
      4. Order of execution of describe and test blocks
      5. General Advice
      6. 下回見

    讀 Jest Doc - 單元測試的結構、執行順序與名詞解釋

    上一篇,聊了關於非同步問題。
    這一篇,我們要更了解 Jest 的生命週期 (其實就是執行順序)。

    廣義的來說,也可以說是測試 3A[1] 的 Arrange

    如果以煮菜來解釋

    1. Arrange: 準備材料
    2. Act: 煮東西
    3. Assert: 試吃,看看結果如何?

    單元測試的血脈

    單元測試框架,最早是由 Smalltalk 的 SUnit[2] 的單元測試框架結構,而成名之作是由 Kent Beck 用 Java 寫的 JUnit,在《重構─改善既有程式的設計, 2/e》一書中有介紹其架構與設計。並且於 2018 再重新出版,《重構|改善既有程式的設計, 2/e》一書中的例子,全面改寫成 JavaScript 而介紹單元測試的章節。

    之後許許多多的單元測試框架,也由不同的語言重新實作,並且命名也有一個慣例[3],而這種重新實作 JUnit 的框架,統稱為 xUnit[4],不過不代表這麼命名的框架為該語言的主流測試框架

    ex:

    • Bash: ShUnit, shUnit2
    • C語言: CUnit
    • C++: CppUnit
    • Objective-C: OCUnit, XCTest
    • Swift: XCTest
    • SystemVerilog: VUnit
    • Java: JUnit
    • JavaScript: JSUnit(Jasmine)
    • jQuery: QUnit
    • PHP: PHPUnit, Codeception

    由於 JUnit 是由物件導向語言而撰寫而成,所以一些非純物件導向語言或可以透過自身語言特性簡化其 xUnit 的類別結構。

    Jest 並不是 xUnit 系列的測試框架

    在此就一邊引用 Jasmine 一邊對照 CppUnit 來看看,在 JavaScript 中的術語如何對到 JUnit 上術語。

    Cppunit下載、編譯、使用與困難排除, p38

    xUnit Jasmine 備註
    TestCase it
    Fixture describe 有 beforeEach,裡面可以加入 TestCase
    Assertion expect + matcher(BDD)
    Suite 檔案的頂層 describe

    測試不多,可以只用 Fixture (Object) + TestCase (method)
    測試變多 xUnit 會使用 Suite (會用 Caller) 將 Fixture::suite() 集結起來,再整包執行。

    主要就先了解到這。
    CppUnit 的測試主程式,一般來說是固定寫法,有需要修改的地方,是輸出的 report 是要印在 terminal 還是寫成 xml 檔案。
    也許 Jasmine 也都寫死了,不用改寫,呼叫時用參數再決定就可以了。

    接下來就來看看 Fixture beforeEach/afterEach 要怎麼在 Jest 寫囉。

    Setup and Teardown

    哪些是先執行,哪些是後執行的?

    在成套的的測試中,往往只差一點點。
    這些測試會有「一致的準備過程」,有時還需要有「一致的收尾」。

    Repeating Setup For Many Tests

    例如: 資料庫的初始與釋放

    beforeEach(() => {
    initializeCityDatabase();
    });

    afterEach(() => {
    clearCityDatabase();
    });

    test('city database has Vienna', () => {
    expect(isCity('Vienna')).toBeTruthy();
    });

    test('city database has San Juan', () => {
    expect(isCity('San Juan')).toBeTruthy();
    });

    如果遇到非同步的準備/釋放過程,可以在 beforeEah/afterEach 中使用 return

    beforeEach(() => {
    return initializeCityDatabase("beforeEach");
    });

    One-Time Setup

    只想要用一次的初始/釋放。
    也可以說只想在第一次的 beforeEach 才有的初始,可以放在 beforeAll
    只想在最後一次的 afterEach 才有的釋放,可以放在 afterAll

    Scoping

    一般而言,before/after 會為每一個測試(test 區塊)進行前置與後置作業。
    但是,如果你想要將測試分類,並且每一類會執行各別的 before/after 區塊的話,可以用 describe

    beforeEach(() => {
    return asyncInitializeCityDatabase("beforeEach VVVVVVV asyncInitializeCityDatabase");
    });

    test('city database has Vienna', () => {
    expect(isCity('Vienna')).toBeTruthy();
    });

    test('city database has San Juan', () => {
    expect(isCity('San Juan')).toBeTruthy();
    });

    describe('matching cities to foods', () => {
    // Applies only to tests in this describe block
    beforeEach(() => {
    return asyncInitializeFoodDatabase("describe beforeEach VVVVVVV asyncInitializeFoodDatabase");
    });

    test('Vienna <3 sausage', () => {
    expect(isValidCityFoodPair('Vienna', 'Wiener Schnitzel')).toBe(true);
    });

    test('San Juan <3 plantains', () => {
    expect(isValidCityFoodPair('San Juan', 'Mofongo')).toBe(true);
    });
    });

    執行順序 (拿掉不必要的資訊)

    # 全域
    beforeEach VVVVVVV asyncInitializeCityDatabase
    > run test Vienna
    beforeEach VVVVVVV asyncInitializeCityDatabase
    > run test San Juan
    beforeEach VVVVVVV asyncInitializeCityDatabase
    # describe 區塊
    describe beforeEach VVVVVVV asyncInitializeFoodDatabase
    > run test Vienna
    beforeEach VVVVVVV asyncInitializeCityDatabase
    describe beforeEach VVVVVVV asyncInitializeFoodDatabase
    > run test San Juan

    如果有多層的 describe
    每一層 describe 外面如果有 beforeEach 的話也會先執行 beforeEach,再執行 describe

    beforeAll(() => console.log('1 - beforeAll'));
    afterAll(() => console.log('1 - afterAll'));
    beforeEach(() => console.log('1 - beforeEach'));
    afterEach(() => console.log('1 - afterEach'));
    test('', () => console.log('1 - test'));
    describe('Scoped / Nested block', () => {
    beforeAll(() => console.log('2 - beforeAll'));
    afterAll(() => console.log('2 - afterAll'));
    beforeEach(() => console.log('2 - beforeEach'));
    afterEach(() => console.log('2 - afterEach'));
    test('', () => console.log('2 - test'));
    });

    // 1 - beforeAll
    // 1 - beforeEach
    // 1 - test
    // 1 - afterEach
    // 2 - beforeAll
    // 1 - beforeEach
    // 2 - beforeEach
    // 2 - test
    // 2 - afterEach
    // 1 - afterEach
    // 2 - afterAll
    // 1 - afterAll

    Order of execution of describe and test blocks

    執行順序!這一段就厲害了,完全超乎你想像的執行順序。
    Jest 在執行 test 區段之前,會先把 describe 執行完。

    一個 describe 執行完,就先把目前搜集的所有 test 跑完。
    這些 test 執行完,再執行接下來會遇到的 describe

    describe('outer', () => {
    console.log('describe outer-a');

    describe('describe inner 1', () => {
    console.log('describe inner 1');
    test('test 1', () => {
    console.log('test for describe inner 1');
    expect(true).toEqual(true);
    });
    });

    console.log('describe outer-b');

    test('test 1', () => {
    console.log('test for describe outer');
    expect(true).toEqual(true);
    });

    describe('describe inner 2', () => {
    console.log('describe inner 2');
    test('test for describe inner 2', () => {
    console.log('test for describe inner 2');
    expect(false).toEqual(false);
    });
    });

    console.log('describe outer-c');
    });

    // describe outer-a
    // describe inner 1
    // describe outer-b
    // describe inner 2
    // describe outer-c
    // test for describe inner 1
    // test for describe outer
    // test for describe inner 2

    General Advice

    建議你,如果遇到測試失敗時,就先使用 test.only() 讓其它的測試先不跑,只跑出錯的,看看是不是還是失敗。

    test.only('this will be the only test that runs', () => {
    expect(true).toBe(false);
    });

    test('this test will not run', () => {
    expect('A').toBe('A');
    });

    通常在測試大型專案的一小部份時出錯,而單獨執行不會失敗,最好檢查一下是不是被其它的測試干擾到了。

    通常檢查需不需要在 beforeEach 加上清除共享資料就可以解決。如果不確定,就先在 beforeEach 記錄這些共享資料。

    下回見

    喜歡的話歡迎訂閱、按讚、分享。
    有任何問題也歡迎在下方留言討論。

    如果想參加聚會的話,可以私訊給我哦~
    我們下一篇見


    1. [Day 3]動手寫Unit Test ↩︎

    2. SUnit - Wikipedia ↩︎

    3. List of unit testing frameworks - Wikipedia ↩︎

    4. xUnit - Wikipedia ↩︎

  • tags:
  • { jest }
  • { async }
  • { promise }
  • { callback }

標籤雲

.new framework 2018鐵人賽 API Doc Aglio Arduino Atom Block Boost Box Model C sharp CRUD CSS CSS Unit CSS3 C_and_Cpp Code Complete 2 Cpp沉思錄 Design Pattern Django ECMA-262 ECMAScript Flex Git HPX HTML Inline JavaScript Jenkins KnR2 LIFF LINE MFC controls MOPCON Media Query OAuth Position Pseudo-elements RWD Raspberry Pi 4 Render tree Rulest Order SVN Selector Sliverlight Specificity TC39 TDD TED TED特區 Transition Ubuntu Vendor Prefix WIN32 API/MFC XML XPath XSLT Xilinx angular1 angularjs animation api assertion async awk axios babel background bash blogger body bootstrap border bugTracker callback canvas chatbot cloud fonts component controller cpp css css-loader database design pattern display docker docsify e2e env eslint exporess express facebook file api file-loader filter flex-grow flex-shrink fontawesome foreign key git git reset git-ftp gitalk google gulp hackmd hash heroku hexo http https husky import iview ivuew javascript jest jquery json keyframes linux login mariadb markdown mentor migration mixin mock model monent nginx ngork node-sass nodejs npm oo opacity orm outline pm2 postman prettier promise protractor proxy pure component python sequelize shell sign-in spy ssl stub sublime text2 swap swing dance test double transition unit test unit testing v-model vee-validate viewport visibility vue vue-cli vue-loader vue-masonry vue-router vue-slot vuex web camp webpack webpack loader which wiki zip zsh 七股宛蓁家兩天一夜 三相整流 三相發電 中柱 人月神話 人機互動 你所不知道的JS 你所不知道的js 再讀一本書 凱宇皓月 動畫特效 十年之後成為大師 台南 和道服說話 圖解柔道之術 團隊溝通技巧 在道館的日子 好想工作室 字型設定 完美 camp 進化論 家家有本難唸的經 小旅行 工業風 影像處理 後搖臂 怦然心動的人生整理魔法 手把開關 抱怨VC6 招募 日舞台南 服三的日子 東岸軍旅 柔道社與我 流浪文章 測試工具 爛 code 收集器 爬蟲 版本控制 狼記事 生平處女秀之電腦裝機 生活小事 直流 CDI 看書 研究所的日子 社大的日子 社服社與我 系統設計 網誌記事 網頁前端技術 舊秋調 藝術 行列輸入法 設計 設計師的路 資料庫 資料庫正規則 踩發桿 追逐我在墾丁*天氣晴 避震器 重構 野狼 鈞祐的獨立山兩天一夜露營 電子DIY 電路 音響拆裝過程 高壓線圈 coil