差異處

這裏顯示兩個版本的差異處。

連向這個比對檢視

Both sides previous revision 前次修改
下次修改
前次修改
web:reactjs:test:teststaticmethod [2019/03/10 16:25]
tony [Reference]
web:reactjs:test:teststaticmethod [2023/06/25 09:48] (目前版本)
行 1: 行 1:
 +{{tag>​reactjs}}
 ====== React - How to test component with static method? ====== ====== React - How to test component with static method? ======
 ===== Problem ===== ===== Problem =====
行 57: 行 58:
 Auth.signOut = auth_do_nothing.bind(Auth);​ Auth.signOut = auth_do_nothing.bind(Auth);​
 </​code>​ </​code>​
-但這寫法存在restore的問題,會將狀態延續至下一個testcase中;因此本篇文章將分享透過[[https://​sinonjs.org/​releases/​latest/​|Sinon]]去mock static method並且清除mock狀態的做法。+但這寫法存在會將狀態延續至下一個testcase中(testsuite似乎沒影響);因此本篇文章將分享透過[[https://​sinonjs.org/​releases/​latest/​|Sinon]]去mock static method並且清除mock狀態的做法。
 ===== How to? ===== ===== How to? =====
 +以Logout為例,我想要模擬使用者點擊Logout並透過Auth.signOut執行登出動作,完整的測試程式碼如下:​
 +<code javascript>​
 +import Logout from '​../​Logout'​
 +import React, { useState, useEffect } from '​react';​
 +import { AuthContext } from '​../​AuthContext';​
 +import { render, fireEvent } from '​react-testing-library';​
 +import Auth from '​../​Auth';​
 +import sinon from '​sinon';​
  
 +let authState = true;
 +function LogoutComp(){
 +    const [ state, setState ] = useState(true);​
 +    useEffect(()=>​{
 +        authState = state;
 +    });
 +    return <​AuthContext.Provider value={[state,​ setState]}><​Logout/></​AuthContext.Provider>;​
 +}
 +
 +describe('<​Logout/>',​()=>​{
 +  let stubSignOut;​
 +  beforeEach(()=>​{
 +    stubSignOut = sinon.stub(Auth,​ '​signOut'​);​
 +  });
 +
 +  afterEach(()=>​{
 +    stubSignOut.restore();​
 +  });
 +
 +  it('​Test logout',​ () => {
 +    // given
 +    const utils = render(<​LogoutComp/>​);​
 +    const logout_nav_item = utils.getByLabelText('​logout-nav-item'​);​
 +
 +    // when
 +    fireEvent.click(logout_nav_item);​
 +
 +    // then
 +    expect(authState).toBe(false);​
 +    expect(stubSignOut.called).toBe(true);​
 +  });  ​
 +});
 +</​code>​
 +在test beforeEach中,我透過sinon.stub去模擬Auth.signOut doNothing的動作,即不給予任何的動作;而在afterEach中,我可以透過stub產生的instance去做restore的動作,讓Auth.signOut回復到模擬之前:​
 +<code javascript>​
 +  let stubSignOut;​
 +  beforeEach(()=>​{
 +    stubSignOut = sinon.stub(Auth,​ '​signOut'​);​
 +  });
 +
 +  afterEach(()=>​{
 +    stubSignOut.restore();​
 +  });
 +</​code>​
 +如果要驗證stub method是否有被呼叫,可以使用下面的做法:​
 +<code javascript>​
 +expect(stubSignOut.called).toBe(true);​
 +</​code>​
 +假如我是在Login form中想要模擬登入失敗的情形呢?​ 這裡我一樣用signOut當範例,我可以在stubSignOut instance上指定要拋出怎樣的例外:​
 +<code javascript>​
 +  it('​Test stub with throw',​ ()=> {
 +    stubSignOut.throws("​name",​ "​message"​);​
 +    try {
 +      Auth.signOut();​
 +      fail("​should be failed"​);​
 +    } catch(e) {
 +      expect(e.name).toBe("​name"​);​
 +      expect(e.message).toBe("​message"​);​
 +    }
 +    expect(stubSignOut.called).toBe(true);​
 +  });
 +</​code>​
 +如果是要用回傳值的方式,則可以透過以下方式:​
 +<code javascript>​
 +  it('​Test stub with return value',​ ()=> {
 +    stubSignOut.returns(true);​
 +    expect(Auth.signOut()).toBe(true);​
 +    expect(stubSignOut.called).toBe(true);​
 +  }); 
 +</​code>​
 +假如我要模擬像signIn有帶參數的情況呢?​ 以login失敗為例,可以使用withArgs來達到這個需求:​
 +<code javascript>​
 +  it('​Test stub with arguments',​ ()=> {
 +    stubSignIn.withArgs('​root',​ '​123456'​).throws("​login failed",​ "wrong password"​);​
 +    try {
 +      Auth.signIn('​root',​ '​123456'​);​
 +      fail("​should be failed"​);​
 +    } catch(e) {
 +      expect(e.name).toBe("​login failed"​);​
 +      expect(e.message).toBe("​wrong password"​);​
 +    }
 +    expect(stubSignIn.called).toBe(true);​
 +  }); 
 +</​code>​
 +可以看到sinon stub可以滿足我們基本的需求,而且使用也相當容易。我唯一在意的是它stub方式,需要將method name傳遞進去;假如以後有rename的需求,這會不容易馬上察覺。針對這個問題,如果之後有看到其它方法,會再分享給各位。
 ===== Reference ===== ===== Reference =====
   * [[https://​sinonjs.org/​releases/​v7.2.7/​stubs/​|Stubs - Sinon.JS]]   * [[https://​sinonjs.org/​releases/​v7.2.7/​stubs/​|Stubs - Sinon.JS]]
   * [[https://​www.robinwieruch.de/​react-fetching-data/​|How to fetch data in React?]]   * [[https://​www.robinwieruch.de/​react-fetching-data/​|How to fetch data in React?]]
  
 +=====    =====
 +----
 +\\
 +~~DISQUS~~