2017-08-01 75 views
1

我是Jest/React初學者。在jest的it我需要等到所有的承諾已經執行之前,實際檢查。如何對提取完成後呈現的React組件進行單元測試?

我的代碼是與此類似:

export class MyComponent extends Component { 
    constructor(props) { 
     super(props); 
     this.state = { /* Some state */ }; 
    } 

    componentDidMount() { 
     fetch(some_url) 
      .then(response => response.json()) 
      .then(json => this.setState(some_state); 
    } 

    render() { 
     // Do some rendering based on the state 
    } 
} 

當組件被安裝時,render()運行兩次:構造運行後一次,並且之後一旦fetch()(在componentDidMount())飾面和鏈接的承諾完成執行) 。

我的測試代碼是與此類似:

describe('MyComponent',() => { 

    fetchMock.get('*', some_response); 

    it('renders something',() => { 
     let wrapper = mount(<MyComponent />); 
     expect(wrapper.find(...)).to.have.something(); 
    }; 
} 

無論我從it返回,在第一次運行後render()執行,但第二次了。例如,如果我返回fetchMock.flush().then(() => expect(...)),則在第二次致電render()(我相信我能理解爲什麼)之前執行返回的承諾。

我該如何等待,直到第二次render()在運行之前調用expect()

+1

這聽起來像你想在一次測試中測試太多東西。你想要測試的是當組件裝入時調用你的讀取函數,然後你有多個其他測試顯式地傳遞給它們,你可以檢查組件是否被正確渲染。 –

+0

@MattWatson如果我檢查(1)fetch函數被調用,並且(2)傳遞狀態正確渲染,那麼我沒有檢查(1.5)狀態是否設置正確。我將如何檢查狀態設置是否正確? –

回答

0

我找到了一種方法做我原本問。我還沒有看到是否是好策略(事實上,之後我不得不重構組件,所以這個問題不再與我正在做的事情有關)。總之,這裏是測試代碼(下文解釋):

import React from 'react'; 
import { mount } from 'enzyme'; 
import { MyComponent } from 'wherever'; 
import fetchMock from 'fetch-mock'; 

let _resolveHoldingPromise = false; 

class WrappedMyComponent extends MyComponent { 

    render() { 
     const result = super.render(); 
     _resolveHoldingPromise && _resolveHoldingPromise(); 
     _resolveHoldingPromise = false; 
     return result; 
    } 

    static waitUntilRender() { 
     // Create a promise that can be manually resolved 
     let _holdingPromise = new Promise(resolve => 
      _resolveHoldingPromise = resolve); 

     // Return a promise that will resolve when the component renders 
     return Promise.all([_holdingPromise]); 
    } 
} 

describe('MyComponent',() => { 

    fetchMock.get('*', 'some_response'); 

    const onError =() => { throw 'Internal test error'; }; 

    it('renders MyComponent appropriately', done => { 
     let component = <WrappedMyComponent />; 
     let wrapper = mount(component); 
     WrappedMyComponent.waitUntilRender().then(
      () => { 
       expect(wrapper.find('whatever')).toBe('whatever'); 
       done(); 
      }, 
      onError); 
    }); 
}); 

主要的想法是,在測試代碼,我的子類成分(如果這是Python的我可能會猴修補它,它的工作原理在這種情況下或多或少相同的方式),以便其render()方法發送它執行的信號。發送信號的方式是通過手動解決承諾。當一個承諾被創建時,它創建兩個函數,解析和拒絕,當被調用時終止承諾。讓承諾超出承諾的方式解決承諾是讓承諾在外部變量中存儲對其解析函數的引用。

感謝fetch-mock作者Rhys Evans向我解釋了手動解析承諾技巧。

1

我會分開關注,主要是因爲更容易維護和測試。而不是在組件中聲明提取,我會在其他地方執行它,例如在redux操作中(如果使用redux)。

然後單獨測試抓取和組件,畢竟這是單元測試。

對於異步測試,您可以在測試中使用done參數。例如:

describe('Some tests',() => { 
    fetchMock.get('*', some_response); 

    it('should fetch data', (done) => { // <---- Param 
    fetchSomething({ some: 'Params' }) 
     .then(result => { 
     expect(result).toBe({ whatever: 'here' }); 
     done(); // <--- When you are done 
     }); 
    }); 
}) 

您可以通過在道具中發送加載的數據來測試您的組件。

describe('MyComponent',() => { 

    it('renders something',() => { 
    const mockResponse = { some: 'data' }; 
    let wrapper = mount(<MyComponent data={mockResponse}/>); 

    expect(wrapper.find(...)).to.have.something(); 
    }); 
}); 

當談到測試,你需要保持它的簡單,如果你的組件是難考,那麼就出錯了設計;)

+0

是否可以像我最初的意圖那樣做?我在問,爲了更好地掌握異步編程。 –

+0

Jest是單元測試,你試圖做的不是單元測試。也就是說,你可以手動設置你的測試狀態,例如'wrapper.setState({some_state})'然後'expect(wrapper.find(...))。to.have.something();'。要測試提取...除了將其從組件中拿走外,此處沒有太多的選項;) – Crysfel

相關問題