2017-08-01 78 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

相关问题