enzymejs / enzyme

JavaScript Testing utilities for React
https://enzymejs.github.io/enzyme/
MIT License
19.95k stars 2.01k forks source link

Can't test value of React Context.Provider? #2529

Closed craigkovatch closed 2 years ago

craigkovatch commented 3 years ago

I searched the Issues history and common issues and didn't find anything speaking to this. There's lots of discussion about how to mock a context, but I'm looking for a way to actually test the value of a Context.Provider.

The render tree of my component looks like this:

<div {...}>
  <TextFieldWidget {...} />
  <ListIsActiveContext.Provider value={focusWithin && activeItemIndex > -1}>
    <List {...} />
  </ListIsActiveContext.Provider>
</div>

JSX transforms context providers into a standard React.createElement call, i.e.:

return React.createElement("div", {...}),
  React.createElement(TextFieldWidget, {...}),
  React.createElement(ListIsActiveContext.Provider, { value: focusWithin && activeItemIndex > -1 },
    React.createElement(List, {...})));

Expected behavior

I expected the following test code to work:

const expectListIsActive = (expectedValue: boolean) => {
  expect(list.find(ListIsActiveContext.Provider).props().value).toBe(expectedValue); // this doesn't work
  expect(listIsActive).toBe(expectedValue);
};

Current behavior

The above code does not work; the ListIsActiveContext.Provider doesn't appear at all in the enzyme .debug() tree output.

Your environment

API

mount

Version

library version
enzyme 3.10.0
react 16.8.6
react-dom 16.8.6
react-test-renderer 16.14.0
adapter (below) 1.15.1

Adapter

enzyme-adapter-react-16

ljharb commented 3 years ago

The way to test a Provider is to use a component that has the Consumer in it. In other words, List itself has to be using the consumer. What does List look like?

shidaying commented 3 years ago

same problem, someone help me please

how to get listPage instance

listPage.js

截屏2021-07-23 12 12 58

listPage.test.js

const setup1 = () => {
    const initialState = {}
    let mockStore = configureStore()
    let store = mockStore(initialState)
    return (
        <Provider store={store}>
            <ListPage />
        </Provider>
    )
}

const setup2 = () => {
    return (
            <ListPage />
    )
}
describe('>>> test', () => {
    it('+++ test', () => {
        const wrapper1 = shallow(setup1())
        console.log(wrapper.debug())
    })
})

setup1 debug 截屏2021-07-23 13 42 39

setup2 debug 截屏2021-07-23 12 15 20

craigkovatch commented 2 years ago

@ljharb that sounds like taking a testing dependency on the implementation details of a consuming component, which seems like a bit of an antipattern to me. I'm additionally confused because IIRC JSX <Context.Provider> just maps to another layer of React.createElement. Must be something I'm missing here because my expectation is that a Provider would work like any other component in the React tree.

ljharb commented 2 years ago

@craigkovatch i'm not sure what you mean. If you have a component that accepts context, the component has already taken on that dependency, and forced all consumers to know that somewhere in the react tree, they need to inject the Provider. Thus, tests need to do the same.

If you don't want to do that, then don't use React Context, because that's precisely how it works.

craigkovatch commented 2 years ago

Ah, ok. So you’re suggesting I write a mock component to test consuming the context value, rather than testing the provider prop directly.

This works, but still seems more complex to me. I know you’ve put lots of thought into the paradigms here — could you help me understand why you don’t think the provider value should (also?) be directly testable in the way I suggested in my original report?

ljharb commented 2 years ago

Either way. It's perfectly fine to test the provider prop directly, because "the provider" is already an implicit dependency of the component.

React simply doesn't provide a way to test Providers directly, outside of the render pipeline (the same is true for hooks, and almost everything else).

craigkovatch commented 2 years ago

Ok, thanks Jordan.