2017-01-19 52 views
4

我已經創建了高階組件/組合組件,以確保在加載組件之前驗證用戶。這是非常基本的,但我有一些麻煩測試它。我想測試下點,這是類似的測試中,我已經在別處:酶測試驗證高級組件(HOC)

  • 渲染組件
  • (我通常通過查找組件特定className校驗)正確props(在我的情況authenticated
  • 渲染包裹元器件如果authenticated並呈現null如果不是

的HOC:

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

import { makeSelectAuthenticated } from 'containers/App/selectors'; 

export default function RequireAuth(ComposedComponent) { 
    class AuthenticatedComponent extends React.Component { 
    static contextTypes = { 
     router: React.PropTypes.object, 
    } 

    static propTypes = { 
     authenticated: React.PropTypes.bool, 
    } 

    componentWillMount() { 
     if (!this.props.authenticated) this.context.router.push('/'); 
    } 

    componentWillUpdate(nextProps) { 
     if (!nextProps.authenticated) this.context.router.push('/'); 
    } 

    render() { 
     return (
     <div className="authenticated"> 
      { this.props.authenticated ? <ComposedComponent {...this.props} /> : null } 
     </div> 
    ); 
    } 
    } 

    const mapStateToProps = createStructuredSelector({ 
    authenticated: makeSelectAuthenticated(), 
    }); 

    return connect(mapStateToProps)(AuthenticatedComponent); 
} 

我使用我的測試enzymejest,但還沒有找到在我的測試成功渲染HOC的一種方式。

任何想法?

解決方案得益於以下回答:

import React from 'react'; 
import { shallow, mount } from 'enzyme'; 
import { Provider } from 'react-redux'; 

import { AuthenticatedComponent } from '../index'; 

describe('AuthenticatedComponent',() => { 
    let MockComponent; 

    beforeEach(() => { 
    MockComponent =() => <div />; 
    MockComponent.displayName = 'MockComponent'; 
    }); 

    it('renders its children when authenticated',() => { 
    const wrapper = shallow(
     <AuthenticatedComponent 
     composedComponent={MockComponent} 
     authenticated={true} 
     />, 
     { context: { router: { push: jest.fn() } } } 
    ); 

    expect(wrapper.find('MockComponent').length).toEqual(1); 
    }); 

    it('renders null when not authenticated',() => { 
    const wrapper = shallow(
     <AuthenticatedComponent 
     composedComponent={MockComponent} 
     authenticated={false} 
     />, 
     { context: { router: { push: jest.fn() } } } 
    ); 

    expect(wrapper.find('MockComponent').length).toEqual(0); 
    }); 
}); 

回答

5

的「刁鑽」這裏的部分是你的HOC返回連接的組件,這使得檢測困難,因爲你已經淺渲染兩層(連接組件和實際的組件),你必須嘲笑的REDX存儲。

相反,您可以預先定義AuthenticatedComponent並將其導出爲命名導出。比你能獨立測試connect像你測試每其他成分:

export class AuthenticatedComponent extends React.Component { 
    static contextTypes = { 
    router: React.PropTypes.object, 
    } 

    static propTypes = { 
    authenticated: React.PropTypes.bool, 
    composedComponent: React.PropTypes.any.isRequired, 
    } 

    componentWillMount() { 
    if (!this.props.authenticated) this.context.router.push('/'); 
    } 

    componentWillUpdate(nextProps) { 
    if (!nextProps.authenticated) this.context.router.push('/'); 
    } 

    render() { 
    const ComposedComponent = this.props.composedComponent; 
    return (
     <div className="authenticated"> 
     { this.props.authenticated ? <ComposedComponent {...this.props} /> : null } 
     </div> 
    ); 
    } 
} 

export default function RequireAuth(ComposedComponent) { 
    const mapStateToProps =() => { 
    const selectIsAuthenticated = makeSelectAuthenticated(); 
    return (state) => ({ 
     authenticated: selectIsAuthenticated(state), 
     composedComponent: ComposedComponent, 
    }); 
    }; 

    return connect(mapStateToProps)(AuthenticatedComponent); 
} 

試驗例:

import React from 'react'; 
import { shallow, mount } from 'enzyme'; 
import { Provider } from 'react-redux'; 
import configureStore from 'redux-mock-store'; 
import RequireAuth, { AuthenticatedComponent } from '../'; 

const Component =() => <div />; 
Component.displayName = 'CustomComponent'; 

const mockStore = configureStore([]); 

describe.only('HOC',() => { 
    const RequireAuthComponent = RequireAuth(Component); 
    const context = { router: { push: jest.fn() } }; 
    const wrapper = mount(
    <Provider store={mockStore({})}> 
     <RequireAuthComponent /> 
    </Provider>, 
    { 
     context, 
     childContextTypes: { router: React.PropTypes.object.isRequired }, 
    } 
); 
    it('should return a component',() => { 
    expect(wrapper.find('Connect(AuthenticatedComponent)')).toHaveLength(1); 
    }); 
    it('should pass correct props',() => { 
    expect(wrapper.find('AuthenticatedComponent').props()).toEqual(
     expect.objectContaining({ 
     authenticated: false, 
     composedComponent: Component, 
     }) 
    ); 
    }); 
}); 

describe('rendering',() => { 
    describe('is authenticated',() => { 
    const wrapper = shallow(
     <AuthenticatedComponent 
     composedComponent={Component} 
     authenticated 
     />, 
     { context: { router: { push: jest.fn() } } } 
    ); 
    it('should render the passed component',() => { 
     expect(wrapper.find('CustomComponent')).toHaveLength(1); 
    }); 
    }); 
    describe('is not authenticated',() => { 
    const wrapper = shallow(
     <AuthenticatedComponent 
     composedComponent={Component} 
     authenticated={false} 
     />, 
     { context: { router: { push: jest.fn() } } } 
    ); 
    it('should not render the passed component',() => { 
     expect(wrapper.find('CustomComponent')).toHaveLength(0); 
    }); 
    }); 
}); 
+0

感謝您的解釋,有可能是你給的如何只呈現一個例子測試中的組件?我已經嘗試了很多使用'shallow'和'mount'的各種方法,但似乎無法獲得任何要正確呈現的測試。 – germainelol

+0

此外,你傳入'mapStateToProps'的'composedComponent'變量有點困惑。那仍然會正常運作? – germainelol

+0

增加了一個如何測試'AuthenticatedComponent'的例子。我同意使用mapStateToProps來傳遞'composedComponent'看起來有點奇怪。由於'AuthenticatedComponent'現在是在HOC之外定義的,我們可以通過它的'ComposedComponent'的唯一方法是通過道具。我想我們可以使用'mapStateToProps'或這個。 – PhilippSpo