前兩天介紹的多半是概念性的內容,相信大家已經迫不及待看到程式碼了吧 ?所以今天會從 D03/static-pages的進度開始寫扣囉 `ヽ(\´▽`)/ `ヽ(´▽`)/ `ヽ(´▽`)/
資料夾結構設計上我希望結合 Next.js 的資料夾路由和 MVC 概念,把對應 view
的 components
和對應 model
的 data
分清楚,所以我們會在 /components
同級目錄下新增 /data
接著再新增一個 /addCourseForm
對應到 AddCourseForm component
最後在 /addCourseForm
新增 textInput.ts
開始前請先安裝 effect
npm i effect
現在請大家回想一下 D03 - 設計資料模型 的 CourseName
還有 Description
,其實參考需求分析完畢後會發現格式根本一模一樣,所以我們可以把它歸納成一個 TextInput
。
import { Data, Equal } from 'effect'
export type TextInput = InitialTextInput | ValidTextInput | InvalidTextInput
export interface InitialTextInput extends Data.Case {
readonly _tag: 'InitialTextInput'
readonly value: ''
}
export interface ValidTextInput extends Data.Case {
readonly _tag: 'ValidTextInput'
readonly value: string
}
export interface InvalidTextInput extends Data.Case {
readonly _tag: 'InvalidTextInput'
readonly value: string
readonly reason: string
}
上面範例擴展的的 Data.Case
可以讓原本不支援物件比對的 JS 物件,變得可以比對
import { Equal } from 'effect'
const result = Equal.equals(textInput1)(textInput)
更重要的是可以幫你產生 constructor
const ofValid = Data.tagged<ValidTextInput>('ValidTextInput')
const courseName1 = ofValid({ value: 'HTML/CSS/JS' })
開始寫測試前再請大家裝個測試工具 vitest
npm i -D vitest
-D
是development dependency
的意思,避免你在發布的時候包了不必要的開發工具
接著在 package.json
新增
"scripts": {
...
"test": "vitest"
...
},
有時候我們很容易把 測試驅動開發 寫成 測試然後開發,要避免路越走越歪,關鍵就在於要把握以下原則
紅 綠 重構
紅 代表新增的功能還沒實作,所以肯定失敗
綠 代表新的的功能通過測試,但還有改進空間
重構 代表把通過測試的功能整理好
AAA
Arange 安排測資
Act 執行測試目標
Assert 驗證執行結果
type getInputString = (event:ChangeEvent) => string // 從輸入事件取出字串
type courseNameOf = (input:string) => CourseName // 把字串傳換成先前定義的資料模型
getInputString
沒有包含商業邏輯,就只是把字串拿出來而已,所以我們先不管它,先測courseNameOf
,也就是現在的 textInputOf
原始碼請參考 D06/unit-test
在以下路徑新增 textInput.spect.ts
檔案
把測試寫上去
import { String } from 'effect'
import { describe, expect, it } from 'vitest'
describe('textInputOf', () => {
const limit = { minLen: 1, maxLen: 50 }
it('should be valid when input length is equal to min length', () => {
const input = 'a' //arrange
const result = textInputOf(limit)(input) //act
expect(result._tag).toBe('ValidTextInput') //assert
})
it('should be valid when input length is equal to max length', () => {
const input = String.repeat(50)('a')
const result = textInputOf(limit)(input)
expect(result._tag).toBe('ValidTextInput')
})
it('should be invalid when input length is greater than max length', () => {
const input = String.repeat(51)('a')
const result = textInputOf(limit)(input)
expect(result._tag).toBe('InvalidTextInput')
})
it('should be invalid when input length is less than min length', () => {
const input = ''
const result = textInputOf(limit)(input)
expect(result._tag).toBe('InvalidTextInput')
})
it('should be invalid when input includes chinese', () => {
const input = '晚安 瑪卡巴卡'
const result = textInputOf(limit)(input)
expect(result._tag).toBe('InvalidTextInput')
})
it('should be invalid when input includes "!"', () => {
const input = 'He110 w0r1d!'
const result = textInputOf(limit)(input)
expect(result._tag).toBe('InvalidTextInput')
})
})
打指令
npm run test
紅燈 !
加入長度驗證,大部分綠燈了 !
加入白名單驗證,全部綠燈 !
時間已經很晚了,今天就先寫到這邊,大家晚安