Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shallowly Get Props from Child Component #848

Closed
dschinkel opened this issue Mar 13, 2017 · 9 comments
Closed

Shallowly Get Props from Child Component #848

dschinkel opened this issue Mar 13, 2017 · 9 comments

Comments

@dschinkel
Copy link

dschinkel commented Mar 13, 2017

I want to check the props of the child, not sure if this is possible with shallow:

export class InterviewWrapper extends Component {
    render(){
        const {company} = this.props;

        return (
            <div id="interview-content" className="margin-top-30">
                <div className="column-group">
                    <div className="all-20">
                         <TableOfContents  />
                    </div>
                    <div className="all-80 margin-top-20">
                        <InterviewContent />
                    </div>
                </div>
            </div>
        )
    }
}

Test

it('renders expected components', () => {
        const company = {
            name: "Stride"
        },
interviewWrapper = shallow(<InterviewWrapper company={company} />),
            tableOfContents = interviewWrapper.find(TableOfContents),
            interviewContent = interviewWrapper.find(InterviewContent);

        // test that InterviewWrapper indeed pass company to TableOfContents.  But trying this is not possible, you can't do instance() on a child component
        expect(tableOfContents.instance().props().company).to.deep.equal(company);
}

Error: ShallowWrapper::instance() can only be called on the root

I tried using instance() on a child component instead of the parent here. Is this possible some other way than how I'm trying to obtain props of a child shallowly? Or do I have no choice but to use mount here?

The goal here isn't to test the behavior of TableOfContents. If I tried doing that, which you probably can't anyway with shallow, would make this an integration test and make my test brittle. Instead, all I care is that the parent component (InterviewWrapper) successfully passed props to TableOfContents and that's the extend of my test here. I want to stop at that. If I am forced to mount this, it essentially becomes an integration test...I don't want that.

But then again, if you're using Shallow() on the parent, is it actually exercising logic in the parent if say the parent had some constants being assigned and passed that constant to a child's props??? If no, then what I'm trying to do makes no sense...

There's still a bit of grey area for me on shallow() in terms of yes I get that it doesn't run behavior, but I guess that means any statements, functions, etc. in the parent and child components are not run when you use shallow correct?

@dschinkel
Copy link
Author

dschinkel commented Mar 13, 2017

Here is another example, lets say you have this:

export class InterviewWrapper extends Component {
    render(){
        const {company} = this.props,
                   anotherCompany = {name: "SoundCloud" }; // whoops someone added this!  and passed this to TableOfContents, wtf are they thinking!!!! :)

        return (
            <div id="interview-content" className="margin-top-30">
                <div className="column-group">
                    <div className="all-20">
                         <TableOfContents company={anotherCompany} />
                    </div>
                    <div className="all-80 margin-top-20">
                        <InterviewContent company={company} />
                    </div>
                </div>
            </div>
        )
    }
}

lets say this errors out with Expected SoundCloud to be Stride (not the subtle change in my assert, it's checking company name below):

it('renders expected components', () => {
        const company = {
            name: "Stride"
        },
interviewWrapper = shallow(<InterviewWrapper company={company} />),
            tableOfContents = interviewWrapper.find(TableOfContents),
            interviewContent = interviewWrapper.find(InterviewContent);

        // test that InterviewWrapper indeed pass company to TableOfContents.  But trying this is not possible, you can't do instance() on a child component
        expect(tableOfContents.instance().props().company.name).to.equal(company.name);
}

So the above, there's some behavior there. The behavior is that for whatever reason, there's some logic there. Logic is simple here, a new constant with another value. It's subtle but it is behavior that could affect things. Any JS you have in your component is behavior...period. We're passing that instead to TableOfContents's company prop. But lets say someone (say me) f'd up the code and instead sent some other company constant into TableOfContent's company property...well that's a bug, our behavior is broken in terms of passing of props.

So when you have behavior like this, I'd wanna test that the outcome (the props passed to the child) is indeed what I expected after sending in initial props to the Parent. And I'm trying to find a way not to have to mount in any of my tests is possible, because this is not an integration test. I have no reason to mount or do anything with the implementation of TableOfContents here. I want it to remain a Unit Test for InterviewWrapper still.

The the most minimal way for me to check that behavior for my parent component in terms of my child would be to test that the child props got what was expected and stop there. Trying to do anything more, any more assertions in this example would turn this test to an integration test..and it would couple (make brittle) my test to things that are not testing this 'unit' (so unit here being the InterviewWrapper component).

@ljharb
Copy link
Member

ljharb commented Mar 14, 2017

You don't need .instance() to get .props(), only to get .props - they're different. Just do .props() after .find.

@dschinkel
Copy link
Author

dschinkel commented Mar 14, 2017

well that's what I tried before trying to add instance() because I was getting undefined for the props, that didn't work on the child with just .props. I get the error

AssertionError: expected undefined to deeply equal { Object (name, images, ...) }

undefined is what I get for props().company

That's for this assert: expect(tableOfContents.props().company).to.deep.equal(company);

So then I tried to add instance() before posting this issue and so yea obviously that doesn't work.
I'm gonna share a video showing what's going on to make the conversation much easier...brb

@ljharb
Copy link
Member

ljharb commented Mar 14, 2017

Try just asserting directly on props() - you might have an actual bug.

@dschinkel
Copy link
Author

dschinkel commented Mar 14, 2017

tried that I get:

AssertionError: expected {} to deeply equal { Object (name, images, ...) }

sec I'm gonna debug in WebStorm again and analyze it. I did that already but gonna take another stab.

@ljharb
Copy link
Member

ljharb commented Mar 14, 2017

Sounds like you're not passing any props at all.

@dschinkel
Copy link
Author

dschinkel commented Mar 14, 2017

Yes I'm the idiot. Thank you for pinpointing the obvious :).

So here was the problem, I actually had this in InterviewWrapper:

<TableOfContents /> - was missing company prop:

<TableOfContents company={company}/>

I must have removed it somehow I don't remember.

@dschinkel
Copy link
Author

dschinkel commented Mar 14, 2017

this works fine now after adding the missing company props on my TableOfContents. And yes props() which I know I've used before many times in other tests is now working fine.

Although, dumb thing I missed, I hope though this helps someone else in the future.

@ljharb ljharb closed this as completed Mar 14, 2017
@ljharb ljharb added the invalid label Mar 14, 2017
@dschinkel
Copy link
Author

I love you enzyme, you rock!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants