抱歉抱歉,這邊的確是我把問題有點搞混了,我來重新整理一下。

我的看法其實跟你說的 decouple 那段一樣,而我上一個評論其實有稍微提到,我怕再講下去有點太長所以就此打住,而且 Medium 不好貼程式碼所以就沒貼了XD

上一個評論我在最後講說:

這部分或許可以透過 mock 或是把 API 放在外層並且再建立一個 wrapper component 來解決,但後者的解法其實就跟 container 一樣了,差別只在於 redux 是統一處理,而這個做法是每個 container 都會呼叫自己的 API 以及管理狀態。

我真正想說的其實是:「為了測試方便,我們會把呼叫 API 的邏輯跟拿來顯示的元件 decouple」。這其實跟 redux 沒有太大關係,我會扯上 redux 是因為這個模式在 redux 最顯而易見,就是 container 與 component 的區別。

如同你所說的一樣,這的確跟 redux 可以不必相關,我同意你所說的這段:

hook + component 等同於 container 的位階, 你對 container/component 怎麼做測試, 對 hooked component/dumb component 就是同樣的作法

然後我所說的 render 就會 call API 那段是我沒有講清楚,那邊所說的是我「以前」的經驗而不是這一篇所講的內容,例如說現在有個頁面需要顯示一個 list,所以我在 componentDidMount 裡面就呼叫 api.getList 來拿清單,這樣就會造成我在寫測試時只要 mount 這個 component 就會去呼叫 API。

我做個簡單的總結好了,我想說的其實是:

為什麼不能從元件裡面直接 call API?其實可以,但你的這個元件必須是像 container 那樣子的元件,而不是把 API 跟 View 混在一起的元件

舉文中提到的 SignIn 當做例子,如果像這樣子把 API 邏輯跟元件的 View 本身寫在一起,在測試時可能就會出現問題。例如說我現在想測試表單 submit 的時候,是否真的有去觸發登入的流程,這應該算是一個滿常見的 test case 吧?

在測試上可能會這樣寫:

const signInComponent = shallow(<SignIn />);
signInComponent.find(‘form’).simulate(‘submit’);
// 接下來,表單 submit 之後如何確定有沒有觸發登入流程?

問題就在於現在登入邏輯直接寫在 component 裡面,所以表單 submit 之後直接去 call API,在這種情形之下就很難確認是否有觸發登入流程。

所以我們才需要把邏輯跟顯示切開,像你的留言提到的那樣:

const { username, password, pending, inputChange, submit } = useSignIn()
function SignInContianer () {
const signInProps = useSignIn()
return <SignInComponent (…signInProps} />
}

這樣子我們就可以很簡單的來測試:

const login = jest.fn()
const signInComponent = shallow(<SignInComponent submit={login} />);
signInComponent.find(‘form’).simulate(‘submit’);
expect(login.mock.calls.length).toBe(1);

只要驗證傳進去的 function 有被呼叫到,就能確認表單 submit 以後有成功觸發登入流程。

一般來說當我看到有人提到:「為什麼不能在 component 裡面直接呼叫 API?」的時候,我所想到的都是第一種情形(混在一起),而不是第二種。

所以我的回答才會是:「因為測試,所以你必須要用第二種方式才方便測試,而 redux 就強迫你要用第二種方式」。

提到 redux 可能把問題混淆了一些,因為拔掉 redux 以後依然可以用第二種方式,不過概念還是跟 redux 預設的 container/component 模式是一樣的。

若是你在問這個問題時心裡所想的其實就是第二種,那我認為是可以的,除了文中所提到的潛在的 memory leak 以外我目前還沒想到什麼大問題。

大概是這樣,希望我這次有解釋清楚XD
然後我對測試其實沒什麼經驗,有錯的話還麻煩不吝指正,感恩。

Written by

重度拖延症患者,興趣是光想不做,有很多想做的事,卻一件都沒有執行。無聊的時候喜歡寫文章,發現自己好像有把事情講得比其他人清楚的能力。相信分享與交流可以讓世界更美好。Medium 文章列表請參考:https://aszx87410.github.io/blog/medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store