# TDD 入門 ##### 研一 / 戴均民 ##### 業二 / 王羿喬 <div style="font-size: 2rem">https://lihi1.cc/5Lajj</div> --- # TDD? --- ## Test-Driven ## Development --- # 測試驅動開發 --- <img src="https://i.imgur.com/Yie6F46.png" style="max-width: 100%; max-height: 100%; border: 0; background: initial;"> --- #### As 一個收銀員 #### I want 一個發票營業稅計算工具 #### so that 我不會再開錯三聯式發票 --- ## 從新增或改寫測試開始 --- ## 需求太抽象怎麼辦 --- ## 用範例來寫需求 ![](https://i.imgur.com/HllTX7f.jpg =300x) --- ### 用未稅價計算營業稅 <style> table.table-2 { font-size: 2.5rem; } .reveal table.table-2 tbody tr th, .reveal table.table-2 tbody tr td { border: 1px solid #ddd; } </style> <table class="table-2"> <tr> <th colspan="2">輸入</th> <th colspan="2">輸出</th> </tr> <tr> <td>稅率 5%</td> <td>未稅價 100 元</td> <td>營業稅 5 元</td> <td>含稅價 105 元</td> </tr> <tr> <td>稅率 5%</td> <td>未稅價 0 元</td> <td>營業稅 0 元</td> <td>含稅價 0 元</td> </tr> <tr> <td>稅率 5%</td> <td>未稅價 16190 元</td> <td>營業稅 810 元</td> <td>含稅價 17000 元</td> </tr> <tr> <td>稅率 5%</td> <td>未稅價 105 元</td> <td>營業稅 5 元</td> <td>含稅價 110 元</td> </tr> <tr> <td>稅率 5%</td> <td>未稅價 10 元</td> <td>營業稅 1 元</td> <td>含稅價 11 元</td> </tr> </table> --- # 原則 ### 沒寫測試以前不能寫任何程式邏輯 --- ```js= // index.js exports.fromExcluded = (rate, excluded) => {} // index.test.js const sut = require('./index') test.each([ [0.05, 100, 5, 105] ])('未稅價計算營業稅,稅率 %d,未稅價 %i 元,營業稅 %i 元,含稅價 %i 元', async (rate, excluded, tax, included) => { const actual = sut.fromExcluded(rate, excluded) expect(actual).toEqual({ rate, excluded, tax, included }) }) ``` --- ![](https://i.imgur.com/kABlHxy.png) --- ## 為什麼只寫了一個測試? --- # 原則 ### 一次只寫出一個沒通過的測試 --- ## 讓測試通過 ```js= // index.js exports.fromExcluded = (rate, excluded) => { return { excluded: 100, included: 105, rate: 0.05, tax: 5 } } ``` --- ![](https://i.imgur.com/7ygqNJX.png) --- ## 程式寫這樣對嗎? --- # 原則 ### 只寫剛好能通過測試的程式 --- ## Over Design 就是浪費 --- ## 由於測試已經通過 ## 所以就繼續加測試 --- ```js= // index.test.js const sut = require('./index') test.each([ [0.05, 100, 5, 105], [0.05, 0, 0, 0] ])('未稅價計算營業稅,稅率 %d,未稅價 %i 元,營業稅 %i 元,含稅價 %i 元', async (rate, excluded, tax, included) => { const actual = sut.fromExcluded(rate, excluded) expect(actual).toEqual({ rate, excluded, tax, included }) }) ``` --- ![](https://i.imgur.com/7Ijjotv.png) --- ## 讓測試通過 ```js= // index.js exports.fromExcluded = (rate, excluded) => { if (excluded === 0) { return { excluded: 0, included: 0, rate: 0.05, tax: 0 } } return { excluded: 100, included: 105, rate: 0.05, tax: 5 } } ``` --- ![](https://i.imgur.com/C7w3YUd.png) --- ## 程式寫這樣對嗎? --- # 重構程式 --- # 原則 ### 只有在測試通過時 才能重構程式 --- ```js= // index.js exports.fromExcluded = (rate, excluded) => { return { excluded, included: excluded * (1 + rate), rate: 0.05, tax: excluded * rate } } ``` --- ![](https://i.imgur.com/zP4YcxB.png) --- ## 再度新增一個失敗的測試 ```js= // index.test.js const sut = require('./index') test.each([ [0.05, 100, 5, 105], [0.05, 0, 0, 0], [0.05, 16190, 810, 17000] ])('未稅價計算營業稅,稅率 %d,未稅價 %i 元,營業稅 %i 元,含稅價 %i 元', async (rate, excluded, tax, included) => { const actual = sut.fromExcluded(rate, excluded) expect(actual).toEqual({ rate, excluded, tax, included }) }) ``` --- ![](https://i.imgur.com/QHKjg2R.png) --- ## 讓測試通過 ```js= // index.js exports.fromExcluded = (rate, excluded) => { return { excluded, included: Math.ceil(excluded * (1 + rate)), rate: 0.05, tax: Math.ceil(excluded * rate) } } ``` --- ![](https://i.imgur.com/qJIM7yz.png) --- ## 再度新增一個失敗的測試 ```js= // index.test.js const sut = require('./index') test.each([ [0.05, 100, 5, 105], [0.05, 0, 0, 0], [0.05, 16190, 810, 17000], [0.05, 105, 5, 110] ])('未稅價計算營業稅,稅率 %d,未稅價 %i 元,營業稅 %i 元,含稅價 %i 元', async (rate, excluded, tax, included) => { const actual = sut.fromExcluded(rate, excluded) expect(actual).toEqual({ rate, excluded, tax, included }) }) ``` --- ![](https://i.imgur.com/TH7JyWL.png) --- ## 讓測試通過 ```js= // index.js exports.fromExcluded = (rate, excluded) => { return { excluded, included: Math.round(excluded * (1 + rate)), rate: 0.05, tax: Math.round(excluded * rate) } } ``` --- ![](https://i.imgur.com/EmpyFqw.png) --- ### 再度新增一個失敗的測試? ```js= // index.test.js const sut = require('./index') test.each([ [0.05, 100, 5, 105], [0.05, 0, 0, 0], [0.05, 16190, 810, 17000], [0.05, 105, 5, 110], [0.05, 10, 1, 11] ])('未稅價計算營業稅,稅率 %d,未稅價 %i 元,營業稅 %i 元,含稅價 %i 元', async (rate, excluded, tax, included) => { const actual = sut.fromExcluded(rate, excluded) expect(actual).toEqual({ rate, excluded, tax, included }) }) ``` --- ![](https://i.imgur.com/YSQUWtD.png) --- ### 用未稅價計算營業稅 <style> table.table-2 { font-size: 2.5rem; } .reveal table.table-2 tbody tr th, .reveal table.table-2 tbody tr td { border: 1px solid #ddd; } </style> <table class="table-2"> <tr> <th colspan="2">輸入</th> <th colspan="2">輸出</th> </tr> <tr> <td>稅率 5%</td> <td>未稅價 100 元</td> <td>營業稅 5 元</td> <td>含稅價 105 元</td> </tr> <tr> <td>稅率 5%</td> <td>未稅價 0 元</td> <td>營業稅 0 元</td> <td>含稅價 0 元</td> </tr> <tr> <td>稅率 5%</td> <td>未稅價 16190 元</td> <td>營業稅 810 元</td> <td>含稅價 17000 元</td> </tr> <tr> <td>稅率 5%</td> <td>未稅價 105 元</td> <td>營業稅 5 元</td> <td>含稅價 110 元</td> </tr> <tr> <td>稅率 5%</td> <td>未稅價 10 元</td> <td>營業稅 1 元</td> <td>含稅價 11 元</td> </tr> </table> --- ## 已經沒有其他測試了 --- ### 就是這個程式已經完成了 --- # 程式碼 <https://github.com/taichunmin/jest-demo-20190807> --- ![](https://i.imgur.com/mzf18W6.png =640x) --- # 課後練習 --- ### 用含稅價計算營業稅 <style> table.table-1 { font-size: 2.5rem; } .reveal table.table-1 tbody tr th, .reveal table.table-1 tbody tr td { border: 1px solid #ddd; } </style> <table class="table-1"> <tr> <th colspan="2">輸入</th> <th colspan="2">輸出</th> </tr> <tr> <td>稅率 5%</td> <td>含稅價 105 元</td> <td>營業稅 5 元</td> <td>未稅價 100 元</td> </tr> <tr> <td>稅率 5%</td> <td>含稅價 0 元</td> <td>營業稅 0 元</td> <td>未稅價 0 元</td> </tr> <tr> <td>稅率 5%</td> <td>含稅價 17000 元</td> <td>營業稅 810 元</td> <td>未稅價 16190 元</td> </tr> <tr> <td>稅率 5%</td> <td>含稅價 110 元</td> <td>營業稅 5 元</td> <td>未稅價 105 元</td> </tr> <tr> <td>稅率 5%</td> <td>含稅價 10 元</td> <td>營業稅 0 元</td> <td>未稅價 10 元</td> </tr> </table> --- <style> .reveal { font-family: "微軟正黑體" } </style> ## Q&A
{"metaMigratedAt":"2023-06-14T23:01:24.487Z","metaMigratedFrom":"YAML","title":"測試驅動開發 TDD 入門","breaks":true,"slideOptions":"{\"transition\":\"slide\",\"theme\":\"moon\",\"allottedMinutes\":60}","contributors":"[{\"id\":\"0d9a5e06-1f92-4142-b9df-fed4c8873573\",\"add\":10809,\"del\":3701}]"}
    1480 views