型生成原理:嵌套對(duì)象、數(shù)組與帶類(lèi)型簽名回調(diào)函數(shù)是如何被 Fake 的)
揭秘 react-fake-props 類(lèi)型生成原理:嵌套對(duì)象、數(shù)組與帶類(lèi)型簽名回調(diào)函數(shù)是如何被 Fake 的【免費(fèi)下載鏈接】react-fake-props Magically generate fake props for your React tests項(xiàng)目地址: https://gitcode.com/gh_mirrors/re/react-fake-propsreact-fake-props 是 React 測(cè)試中的假 props(mock props)自動(dòng)生成工具:傳入組件文件路徑,它就能基于 TypeScript、Flow 或 PropTypes 的類(lèi)型定義,遞歸產(chǎn)出一組可直接{...props}展開(kāi)渲染的測(cè)試數(shù)據(jù)。本文拆解它的類(lèi)型生成原理,重點(diǎn)看嵌套對(duì)象、數(shù)組、帶類(lèi)型簽名的回調(diào)函數(shù)這三類(lèi)深水區(qū)類(lèi)型分別是如何被 Fake 的。一、react-fake-props 是什么:React 測(cè)試假 Props 自動(dòng)生成工具 一句話:把組件的 props 類(lèi)型讀出來(lái),再按類(lèi)型自動(dòng)填上合理的假值,省去手寫(xiě)測(cè)試數(shù)據(jù)。支持 3 種 props 類(lèi)型系統(tǒng):TypeScript、Flow、PropTypes常與 Jest 快照測(cè)試、Enzyme 渲染配合使用默認(rèn)只生成必填 prop;傳{ optional: true }才連可選 prop 一起生成整個(gè)核心實(shí)現(xiàn)只有 280 多行,全部集中在src/index.js,這也是本文解剖的主角。二、整體流程:一次 fakeProps() 調(diào)用背后發(fā)生了什么流程分 3 步,入口是src/index.js的模塊導(dǎo)出函數(shù)(約 L259-L284):讀文件:fs.readFileSync讀取組件源碼;解析類(lèi)型樹(shù):交給 react-docgen 解析,得到每個(gè) prop 的類(lèi)型描述(tsType/flowType/ 普通 propTypes 描述);遞歸生成假值:fakeDataForProps()(L246-L257)遍歷頂層 prop,只處理required的(或開(kāi)啟 optional 的),再交給分發(fā)函數(shù)。關(guān)鍵在于它維護(hù)了兩條并行的類(lèi)型分發(fā)管線:類(lèi)型系統(tǒng)分發(fā)函數(shù)分支依據(jù)PropTypesgetFakePropType()prop.type.nameFlow / TypeScriptgetFakeTyped()type.namegetFakeProp()(L240-L244)只做一件事:看這個(gè) prop 有沒(méi)有flowType或tsType,有就走 Flow/TS 管線,沒(méi)有就走 PropTypes 管線。三套類(lèi)型體系由此被統(tǒng)一到一個(gè)入口。三、嵌套對(duì)象是如何被 Fake 的:遞歸展開(kāi) 路徑前綴TS / Flow:signature.properties 逐層拆開(kāi)以fixtures/typescript/Component.tsx中的Person類(lèi)型為例,person里又嵌套了address: Address、repos: ArrayRepo。react-docgen 解析后,每個(gè)具名類(lèi)型都會(huì)變成帶signature.properties的結(jié)構(gòu),由fakeSignature()(L148-L192)的 object 分支處理:過(guò)濾出required(或 optional 開(kāi)啟時(shí)全保留)的屬性;逐個(gè)遞歸調(diào)用getFakeTyped(prefix . prop.key, ...),一層層把Person → Address → Commit全部展開(kāi)成真實(shí)對(duì)象。路徑前綴:字符串假值的指紋?fakeString()的返回值就是傳入的 prefix 本身,所以每個(gè)字符串 prop 的假值 它的完整點(diǎn)分路徑:person.address.street → person.address.street person.repos.url → person.repos.url這個(gè)設(shè)計(jì)有兩個(gè)好處:任意深度的字符串都天然唯一,快照對(duì)比時(shí)一眼能看出差異在哪一層;斷言時(shí)推薦寫(xiě)props.person.name而不是硬編碼字符串,因?yàn)檩敵龈袷娇赡茈S版本變化(README 的 Tip 一節(jié)也是這么建議的)。PropTypes 的嵌套走getShape()(L30-L41),邏輯完全同構(gòu):遍歷 shape 的 key,按required || optional過(guò)濾,前綴拼接后遞歸。fixtures/propTypes/NestedShape.jsx的兩層嵌套shape,在快照里生成了requiredShape.nestedRequiredShape.optionalString這樣的值。四、數(shù)組是如何被 Fake 的:為什么永遠(yuǎn)只有一個(gè)元素?cái)?shù)組的 Fake 策略出奇地一致——只放一個(gè)元素:聲明方式生成結(jié)果實(shí)現(xiàn)位置TSArrayRepo[{ url: ..., ... }]getFakeTyped()的Array分支(L213-L230)PropTypesarrayOf(shape(...))[ { 假對(duì)象 } ]getArrayOf()(L22-L24)無(wú)元素類(lèi)型的裸數(shù)組[]fakeArray()(L51-L53)實(shí)現(xiàn)上,TS 的ArrayT解析后帶elements字段,代碼對(duì)每個(gè)元素類(lèi)型遞歸getFakeTyped()生成數(shù)組。注意前綴跨數(shù)組時(shí)不加下標(biāo),所以ArrayRepo里元素的字符串前綴直接是person.repos,而不是person.repos[0]。為什么只要一個(gè)元素?對(duì)渲染測(cè)試來(lái)說(shuō),列表能畫(huà)出一項(xiàng)就足以覆蓋布局邏輯,輸出更簡(jiǎn)潔、快照更穩(wěn)定。五、帶類(lèi)型簽名的回調(diào)函數(shù):動(dòng)態(tài)構(gòu)造一個(gè)真函數(shù)這是整個(gè)庫(kù)里最有意思的機(jī)制。普通PropTypes.func只會(huì)得到一個(gè)空函數(shù);但 TS/Flow 里聲明了簽名的回調(diào)(如getFriend?: (person: Person) Person),react-docgen 會(huì)解析出完整的signature(參數(shù)名列表 返回類(lèi)型)。fakeTypedFunction()(L132-L146)就利用這一點(diǎn):從signature.arguments取出每個(gè)參數(shù)名;遞歸 Fake 出返回類(lèi)型的完整假值,并JSON.stringify凍結(jié)成代碼文本;用Function.apply(null, [...參數(shù)名, return 凍結(jié)的返回值])動(dòng)態(tài)構(gòu)造出一個(gè)真正可調(diào)用的函數(shù)。也就是說(shuō),生成 props 里的getFriend不是擺設(shè):調(diào)用allProps.getFriend()真的會(huì)返回一個(gè)假Person,即{ name: getFriend.name, age: 1, ... }。注意返回對(duì)象里的字符串前綴用的是函數(shù)名getFriend,因?yàn)檫f歸時(shí)傳入的 prefix 就是這個(gè) prop 的名字。__tests__/typescript.js里就有專(zhuān)門(mén)用例驗(yàn)證這一點(diǎn)(L25-L32)。若回調(diào)沒(méi)有類(lèi)型簽名(比如 PropTypes 的func),則回退到fakeFunction(),返回一個(gè)什么都不做的空函數(shù),保證組件至少能正常渲染。六、optional 開(kāi)關(guān)與兜底類(lèi)型optional:默認(rèn)只 Fakerequired的 prop;傳{ optional: true }后,fakeDataForProps()、getShape()、fakeSignature()三處過(guò)濾條件都會(huì)放行可選字段,生成的對(duì)象會(huì)明顯變大。不支持的類(lèi)型:instanceOf和 custom 校驗(yàn)器無(wú)法憑空造出合法實(shí)例,庫(kù)會(huì)生成一句人話提示,如instanceOf type not supported, please set the correct value for X prop,提醒手動(dòng)補(bǔ)值(fakeInstanceOf()L75-L77、fakeCustom()L83-L85)。all: true:一個(gè)文件導(dǎo)出多個(gè)組件時(shí),改用 react-docgen 的findAllComponentDefinitions解析器,返回{ displayName, props }數(shù)組,連未導(dǎo)出的組件也能拿到(L263-L276)。七、關(guān)鍵文件導(dǎo)航 想動(dòng)手驗(yàn)證,這幾個(gè)文件值得一看:src/index.js—— 核心實(shí)現(xiàn):類(lèi)型分發(fā)、遞歸生成、動(dòng)態(tài)函數(shù)構(gòu)造全在這里fixtures/typescript/Component.tsx—— 嵌套對(duì)象 數(shù)組 簽名回調(diào)的完整示例類(lèi)型fixtures/propTypes/NestedShape.jsx—— PropTypes 嵌套 shape 示例__tests__/typescript.js—— 斷言getFriend()返回值、前綴命名等行為的測(cè)試__tests__/__snapshots__/typescript.js.snap—— 完整的 Fake 輸出快照,直接看效果README.md—— API 與使用建議(含props.A斷言技巧)一句話總結(jié):react-fake-props 的類(lèi)型生成 用 react-docgen 拿到類(lèi)型樹(shù) 雙管線遞歸分發(fā) 路徑前綴命名 數(shù)組只留一個(gè)元素 動(dòng)態(tài)構(gòu)造簽名函數(shù),用 280 行代碼覆蓋了 TS、Flow、PropTypes 三條體系的深水區(qū)?!久赓M(fèi)下載鏈接】react-fake-props Magically generate fake props for your React tests項(xiàng)目地址: https://gitcode.com/gh_mirrors/re/react-fake-props創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考