2016-02-01 97 views
37

我正在使用Mocha,Chai,Karma,Sinon,Webpack进行单元测试。如何测试React-Redux连接组件?

我跟着这个链接来配置我的React-Redux代码的测试环境。

https://medium.com/@scbarrus/how-to-get-test-coverage-on-react-with-karma-babel-and-webpack-c9273d805063#.7kcckz73r

我可以成功地测试自己的行动和减速器javascript代码,但是当涉及到我的测试组件总是抛出一些错误。

import React from 'react'; 
import TestUtils from 'react/lib/ReactTestUtils'; //I like using the Test Utils, but you can just use the DOM API instead. 
import chai from 'chai'; 
// import sinon from 'sinon'; 
import spies from 'chai-spies'; 

chai.use(spies); 

let should = chai.should() 
    , expect = chai.expect; 

import { PhoneVerification } from '../PhoneVerification'; 

let fakeStore = { 
     'isFetching': false, 
     'usernameSettings': { 
     'errors': {}, 
     'username': 'sahil', 
     'isEditable': false 
     }, 
     'emailSettings': { 
     'email': '[email protected]m', 
     'isEmailVerified': false, 
     'isEditable': false 
     }, 
     'passwordSettings': { 
     'errors': {}, 
     'password': 'showsomestarz', 
     'isEditable': false 
     }, 
     'phoneSettings': { 
     'isEditable': false, 
     'errors': {}, 
     'otp': null, 
     'isOTPSent': false, 
     'isOTPReSent': false, 
     'isShowMissedCallNumber': false, 
     'isShowMissedCallVerificationLink': false, 
     'missedCallNumber': null, 
     'timeLeftToVerify': null, 
     '_verifiedNumber': null, 
     'timers': [], 
     'phone': '', 
     'isPhoneVerified': false 
     } 
} 

function setup() { 
    console.log(PhoneVerification); 
    // PhoneVerification.componentDidMount = chai.spy(); 
    let output = TestUtils.renderIntoDocument(<PhoneVerification {...fakeStore}/>); 
    return { 
     output 
    } 
} 

describe('PhoneVerificationComponent',() => { 
    it('should render properly', (done) => { 
     const { output } = setup(); 
     expect(PhoneVerification.prototype.componentDidMount).to.have.been.called; 
     done(); 
    }) 
}); 

下面的错误出现在上面的代码。

FAILED TESTS: 
    PhoneVerificationComponent 
    ✖ should render properly 
     Chrome 48.0.2564 (Mac OS X 10.11.3) 
    Error: Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. 

尝试从空子间谍变为chai间谍。

我该如何测试我的React-Redux Connected Components(智能组件)?

+0

如何导出组件?是使用命名导出还是仅导出默认导入{PhoneVerification} ../ PhoneVerification';'是你的冒犯路线,当你这样做时,如果你没有进行命名导出,你得到undefined –

+0

我正在使用命名导出 – Ayushya

+0

我也有一个类似的设置,并得到类似的错误信息。对此有何进展?谢谢。 – FujiRoyale

回答

35

一个更漂亮的方法来做到这一点,是导出你的普通组件和包装在connect中的组件。命名的出口将是组件,默认是被包装的成分:

通过这种方式,你可以在你的应用程序正常进口,但是当涉及到测试时,可以使用import { Sample } from 'component'导入名为export。

+0

非常感谢,它的工作原理! – Ayushya

+0

你能否接受我的文章作为答案? –

+0

伟大的解决方案。谢谢! –

1

尝试创建2个文件,其中一个包含组件本身,不知道任何存储或任何内容(PhoneVerification-component.js)。然后,你会在你的应用程序中使用,并且第二个(PhoneVerification.js),只能通过connect函数返回订阅存储第一成分,像

import PhoneVerificationComponent from './PhoneVerification-component.js' 
import {connect} from 'react-redux' 
... 
export default connect(mapStateToProps, mapDispatchToProps)(PhoneVerificationComponent) 

然后你就可以通过要求测试你的“哑巴”成分PhoneVerification-component.js在你的测试中,并提供必要的模拟道具。没有测试已经测试的点(连接装饰器,mapStateToProps,mapDispatchToProps等...)

+0

感谢您的解决方案。 – Ayushya

+0

嘿@ george.cz,你描述'mapStateToProps'为“已经测试过。”你怎么样测试'mapStateToProps'和'mapDispatchToProps'?你从容器中导出它们并且为导出的函数创建测试? – stone

+1

@skypecakes我相信他的意思是['connect'本身](https://github.com/reactjs/react-redux/blob/master/test/components/connect.spec.js)。 – falsarella

10

你可以测试你连接的组件,我认为你应该这样做。您可能首先要测试未连接的组件,但是我建议您在测试连接的组件的情况下不会有完整的测试覆盖率。

下面是我用Redux和Enzyme做的一个未经测试的提取物。中心思想是使用Provider将测试状态连接到测试中的连接组件。

import { Provider } from 'react-redux'; 
import configureMockStore from 'redux-mock-store'; 
import SongForm from '../SongForm'; // import the CONNECTED component 

// Use the same middlewares you use with Redux's applyMiddleware 
const mockStore = configureMockStore([ /* middlewares */ ]); 
// Setup the entire state, not just the part Redux passes to the connected component. 
const mockStoreInitialized = mockStore({ 
    songs: { 
     songsList: { 
      songs: { 
       songTags: { /* ... */ } 
      } 
     } 
    } 
}); 

const nullFcn1 =() => null; 
const nullFcn2 =() => null; 
const nullFcn3 =() => null; 

const wrapper = mount(// enzyme 
     <Provider store={store}> 
      <SongForm 
      screen="add" 
      disabled={false} 
      handleFormSubmit={nullFcn1} 
      handleModifySong={nullFcn2} 
      handleDeleteSong={nullFcn3} 
      /> 
     </Provider> 
    ); 

const formPropsFromReduxForm = wrapper.find(SongForm).props(); // enzyme 
expect(
     formPropsFromReduxForm 
    ).to.be.deep.equal({ 
     screen: 'add', 
     songTags: initialSongTags, 
     disabled: false, 
     handleFormSubmit: nullFcn1, 
     handleModifySong: nullFcn2, 
     handleDeleteSong: nullFcn3, 
     }); 

===== ../SongForm.js 

import React from 'react'; 
import { connect } from 'react-redux'; 

const SongForm = (/* object */ props) /* ReactNode */ => { 
    /* ... */ 
    return (
     <form onSubmit={handleSubmit(handleFormSubmit)}> 
      .... 
     </form> 

}; 

const mapStateToProps = (/* object */ state) /* object */ => ({ 
    songTags: state.songs.songTags 
}); 
const mapDispatchToProps =() /* object..function */ => ({ /* ... */ }); 

export default connect(mapStateToProps, mapDispatchToProps)(SongForm) 

您可能希望创建与纯Redux的商店。 redux-mock-store只是一个轻量级的版本,它用于测试。

您可能想使用react-addons-test-utils代替airbnb的酶。

我使用airbnb的chai-enzyme来获得React-aware预期选项。这个例子中不需要。

+0

对不起@Tarjei Huse,但我回滚了你的编辑。 是Redux提供的挂钩到React中的东西。目前,你的编辑是硬编码的。不过,我不希望我的代码暴露在以前改变它的实现。 – JohnSz

0

接受答案的问题是,我们正在输出一些不必要的东西,只是为了能够测试它。在我看来,出口一门课只是为了测试它并不是一个好主意。

这里是无需出口任何东西,但连成分的简洁的解决方案:

如果使用的是笑话,你可以嘲笑connect方法返回三件事情:

  1. mapStateToProps
  2. mapDispatchToProps
  3. ReactComponent

这样做很简单。有两种方法:内联模拟或全局模拟。

1.使用内嵌模拟

测试的描述函数之前添加以下代码段。

jest.mock('react-redux',() => { 
 
    return { 
 
    connect: (mapStateToProps, mapDispatchToProps) => (reactComponent) => ({ 
 
     mapStateToProps, 
 
     mapDispatchToProps, 
 
     ReactComponent 
 
    }), 
 
    Provider: ({ children }) => children 
 
    } 
 
})

2.使用文件模拟

  1. 创建在根(其中的package.json位于)的文件__mocks__/react-redux.js
  2. 添加下面的代码片断的文件中。

module.exports = { 
 
    connect: (mapStateToProps, mapDispatchToProps) => (reactComponent) => ({ 
 
    mapStateToProps, 
 
    mapDispatchToProps, 
 
    ReactComponent, 
 
    }), 
 
    Provider: ({children}) => children 
 
};

嘲讽之后,你就可以访问所有的上述三种使用Container.mapStateToPropsContainer.mapDispatchToPropsContainer.ReactComponent

集装箱可以通过简单地做

import Container from '<path>/<fileName>.container.js'

希望它可以帮助进口。

请注意,如果您使用文件模拟。模拟文件将在全球范围内用于所有测试用例(除非在测试用例之前执行jest.unmock('react-redux'))