2016-06-30 12 views
19

Mam dość proste reagują składnik (wrapper link, który dodaje klasy „aktywny”, jeśli trasa jest aktywna):Jak reagować kpić-Router kontekst

import React, { PropTypes } from 'react'; 
import { Link } from 'react-router'; 

const NavLink = (props, context) => { 
    const isActive = context.router.isActive(props.to, true); 
    const activeClass = isActive ? 'active' : ''; 

    return (
    <li className={activeClass}> 
     <Link {...props}>{props.children}</Link> 
    </li> 
); 
} 

NavLink.contextTypes = { 
    router: PropTypes.object, 
}; 

NavLink.propTypes = { 
    children: PropTypes.node, 
    to: PropTypes.string, 
}; 

export default NavLink; 

Jak mam go przetestować? Moja jedyna próba była:

import NavLink from '../index'; 

import expect from 'expect'; 
import { mount } from 'enzyme'; 
import React from 'react'; 

describe('<NavLink />',() => { 
    it('should add active class',() => { 
    const renderedComponent = mount(<NavLink to="/home" />, { router: { pathname: '/home' } }); 
    expect(renderedComponent.hasClass('active')).toEqual(true); 
    }); 
}); 

To nie działa i zwraca TypeError: Cannot read property 'isActive' of undefined. Zdecydowanie potrzebuje drwiny z routera, ale nie mam pojęcia, jak to napisać.

Odpowiedz

16

Dzięki @Elon Szopos za odpowiedź, ale udaje mi się napisać coś o wiele bardziej proste (po https://github.com/airbnb/enzyme/pull/62):

import NavLink from '../index'; 

import expect from 'expect'; 
import { shallow } from 'enzyme'; 
import React from 'react'; 

describe('<NavLink />',() => { 
    it('should add active class',() => { 
    const context = { router: { isActive: (a, b) => true } }; 
    const renderedComponent = shallow(<NavLink to="/home" />, { context }); 
    expect(renderedComponent.hasClass('active')).toEqual(true); 
    }); 
}); 

muszę zmienić mount do shallow aby nie oceniać Link co daje mi błąd połączony z routerem reagującym TypeError: router.createHref is not a function.

Wolałbym "prawdziwy" router reagujący niż tylko obiekt, ale nie mam pojęcia, jak go utworzyć.

+1

Zauważ, że nawiasy klamrowe w "kontekście" zostały przerzucone na płytkie - początkowo tęskniłem za nimi i zajęło mi trochę czasu, aby dowiedzieć się, dlaczego to nie działa! –

5

Testowanie komponentów zależnych od kontekstu może być nieco trudne. To, co zrobiłem, to napisanie wrappera, którego użyłem w moich testach.

można znaleźć poniżej wrapper:

import React, { PropTypes } from 'react' 

export default class WithContext extends React.Component { 
    static propTypes = { 
    children: PropTypes.any, 
    context: PropTypes.object 
    } 

    validateChildren() { 
    if (this.props.children === undefined) { 
     throw new Error('No child components were passed into WithContext') 
    } 
    if (this.props.children.length > 1) { 
     throw new Error('You can only pass one child component into WithContext') 
    } 
    } 

    render() { 
    class WithContext extends React.Component { 
     getChildContext() { 
     return this.props.context 
     } 

     render() { 
     return this.props.children 
     } 
    } 

    const context = this.props.context 

    WithContext.childContextTypes = {} 

    for (let propertyName in context) { 
     WithContext.childContextTypes[propertyName] = PropTypes.any 
    } 

    this.validateChildren() 

    return (
     <WithContext context={this.props.context}> 
     {this.props.children} 
     </WithContext> 
    ) 
    } 
} 

Tutaj można zobaczyć przykład użycia:

<WithContext context={{ location: {pathname: '/Michael/Jackson/lives' }}}> 
    <MoonwalkComponent /> 
    </WithContext> 

    <WithContext context={{ router: { isActive: true }}}> 
    <YourTestComponent /> 
    </WithContext> 

i powinno działać jak można by oczekiwać.

3

Można użyć https://github.com/pshrmn/react-router-test-context do tego celu dokładnego

„Tworzenie pseudo kontekstowe obiektu, który duplikaty reagować strukturę context.router routera. Funkcja ta jest przydatna do płytkiego testów jednostkowych z enzymem.”

Po zainstalowaniu go, będzie w stanie zrobić coś takiego

describe('my test',() => { 
    it('renders',() => { 
    const context = createRouterContext() 
    const wrapper = shallow(<MyComponent />, { context }) 
    }) 
}) 
11

Dla reagują routera v4 można użyć <MemoryRouter>. Przykład z AVA i enzym:

import React from 'react'; 
import PropTypes from 'prop-types'; 
import test from 'ava'; 
import { mount } from 'enzyme'; 
import sinon from 'sinon'; 
import { MemoryRouter as Router } from 'react-router-dom'; 

const mountWithRouter = node => mount(<Router>{node}</Router>); 

test('submits form directly', t => { 
    const onSubmit = sinon.spy(); 
    const wrapper = mountWithRouter(<LogInForm onSubmit={onSubmit} />); 
    const form = wrapper.find('form'); 
    form.simulate('submit'); 

    t.true(onSubmit.calledOnce); 
}); 
+4

To działa, dopóki nie trzeba aktualizować rekwizytów zawiniętego komponentu. Wywołanie 'setProps()' działa tylko na głównym komponencie, który jest teraz 'MemoryRouter' –

+0

. Myślę, że niemożliwe jest znalezienie uniwersalnego sposobu testowania komponentów. Jednak w powyższym przykładzie możliwe jest ustawienie właściwości w linii. Jak 'onSubmit'. – walteronassis

0

Musisz znać różnicę między mount & shallow. official documentation wyjaśnił Płytki Render:

Pisząc testy jednostkowe dla React, płytkie rendering może być pomocny. Niewielkie renderowanie pozwala renderować komponent "jeden poziom głęboki" i potwierdzać fakty o tym, co zwraca jego metoda renderowania, nie martwiąc się o zachowanie komponentów potomnych, które nie są tworzone instancjami ani nie są renderowane w postaci . To nie wymaga DOM.

następnie bez komplikacji ani konteksty:

const renderedComponent = shallow(<NavLink to="/home" />); 

zamiast

const renderedComponent = mount(<NavLink to="/home" />, { router: { pathname: '/home' } }); 

będzie działać!