2016-06-13 11 views
21

Mam komponent SampleComponent, który montuje kolejny "podłączony komponent" (tj. container). Kiedy próbuję przetestować SampleComponent przez mount ING (ponieważ muszę componentDidMount), pojawia się błąd:Testowanie komponentów zagnieżdżonych za pomocą Enzymu wewnątrz React & Redux

Invariant Violation: Could not find "store" in either the context or props of "Connect(ContainerComponent)". Either wrap the root component in a , or explicitly pass "store" as a prop to "Connect(ContainerComponent)".

Jaki jest najlepszy sposób na sprawdzenie tego?

Odpowiedz

4

Co ja w zasadzie nie było przynieść w moim redux sklepu (i Provider) i owinął je w komponencie użytkowego następująco:

export const CustomProvider = ({ children }) => { 
    return (
    <Provider store={store}> 
     {children} 
    </Provider> 
); 
}; 

wtedy, ja mount z SampleComponent i uruchomić testy przeciwko niej:

it('contains <ChildComponent/> Component',() => { 
    const wrapper = mount(
    <CustomProvider> 
     <SampleComponent {...defaultProps} /> 
    </CustomProvider> 
); 
    expect(wrapper.find(ChildComponent)).to.have.length(1); 
}); 
+0

Widzę, że używasz mounta, jeśli spróbuję zastąpić '' mount'' '' shallo'', pojawi się błąd. czy ty też to zauważyłeś? – Mehrdad

+2

Ta odpowiedź działa w niektórych przypadkach, ale nie działa, gdy trzeba przetestować cykl życia komponentu. Na przykład wywołanie 'wrapper.setProps()' nie wyzwoli 'componentWillReceiveProps()' w 'SampleComponent'. –

25

Uchwyt enzymatyczny przyjmuje parametry opcjonalne. Te dwie, które są niezbędne do tego, co trzeba są

options.context: (Object [optional]): Context to be passed into the component

options.childContextTypes: (Object [optional]): Merged contextTypes for all children of the wrapper byłoby zamontować SampleComponent z opcji obiekt tak:

const store = { 
    subscribe:() => {}, 
    dispatch:() => {}, 
    getState:() => ({ ... whatever state you need to pass in ... }) 
} 
const options = { 
    context: { store }, 
    childContextTypes: { store: React.PropTypes.object.isRequired } 
} 

const _wrapper = mount(<SampleComponent {...defaultProps} />, options) 

Teraz twój SampleComponent minie kontekst podałeś dół .

+2

To jest idealne! Podczas gdy zaakceptowana odpowiedź działa przez większość czasu, wadą jest to, że tracisz możliwość używania mount api w pełni. Na przykład użycie akceptowanej odpowiedzi na owijanie komponentu w 'Providencie' nie pozwoli na użycie api' wrapper.state() '. To rozwiązanie zapewni pełną gamę metod dla opakowania. – neurosnap

+0

To jest lepsza odpowiedź niż zaakceptowana odpowiedź, z powodów podanych powyżej (tj. Zamontowany wrapper nie jest faktycznie elementem, który próbujesz przetestować), a także dlatego, że wtedy możesz użyć symulowanego sklepu zamiast rzeczywistego sklepu, pozostawiając wszystkie redux z równania. – GTF

+2

ten opcjonalny argument nie jest w dokumentach, jak go znalazłeś? w kodzie? –

1

Można użyć nazwy eksportu, aby rozwiązać ten problem:

Trzeba:

class SampleComponent extends React.Component{ 
... 
    render(){ 
     <div></div> 
    } 
} 

export default connect(mapStateToProps, mapDispatchToProps)(SampleComponent) 

Możesz dodać eksport przed klasą:

export class SampleComponent extends React.Component{ 

i zaimportować ten składnik bez sklep redukcyjny:

import { SampleComponent } from 'your-path/SampleComponent'; 

Dzięki temu rozwiązaniu nie trzeba importować magazynu do plików testowych.

1

Opcja 1) W ramach testu można zawinąć komponent kontenera z komponentem dostawcy React-Redux. Tak więc przy takim podejściu odwołujesz się do sklepu, przekazujesz go dostawcy i komponujesz testowany komponent w środku. Zaletą tego podejścia jest możliwość utworzenia niestandardowego sklepu dla testu. Takie podejście jest przydatne, jeśli chcesz przetestować części swojego składnika związane z Redux. Opcja 2) Może nie dbasz o testowanie elementów związanych z Redux. Jeśli jesteś zainteresowany testowaniem renderowania komponentu i zachowań związanych z lokalnym stanem, możesz po prostu dodać eksport nazwany dla niepołączonej wersji prostej komponentu. Aby wyjaśnić, kiedy dodajesz słowo kluczowe "eksportuj" do swojej klasy, mówisz, że teraz klasa może zostać zaimportowana na dwa sposoby z nawiasami klamrowymi {} lub nie. Przykład:

export class MyComponent extends React.Component{ render(){ ... }} 

... 

export default connect(mapStateToProps, mapDispatchToProps)(MyComponent) 

później na pliku testowego:

import MyComponent from 'your-path/MyComponent'; // it needs a store because you use "default export" with connect 
import {MyComponent} from 'your-path/MyComponent'; // don't need store because you use "export" on top of your class. 

Mam nadzieję, że pomaga ktoś tam.

Powiązane problemy