Enzyme: Better way to assert on rendered HTML properties?

Created on 17 Oct 2016  路  20Comments  路  Source: enzymejs/enzyme

I found a way to assert on rendered HTML attributes, but found the syntax to be a bit verbose.

For example, when trying to assert that a component renders an html input tag with a maxlength property of 10:

var wrapper = mount(
  <SomeComponent
    ...
    maxLength={10}
    />
);
var rendered = wrapper.find('input').render();

expect(rendered.find('input')[0].attribs.maxlength).toEqual('10');

Is there a better way to do the expect? I looked through both the enzyme API and Cheerio API and couldn't really find anything more simple. If not, does anyone have any recommendations on a syntax that would be preferred?

Maybe something like:

expect(rendered.htmlProp('maxlength')).toEqual('10');

Most helpful comment

I simultaneously agree and disagree. I agree that it's the component's job to set the prop on input, and React's job to pass the prop through to the HTML element.

However, a user is going to see the HTML, so that's really what I need to make sure continues to work over time, both as my codebase evolves, and as I update React to the latest version.

If not by testing the HTML output, how do you test for this? Or do you think that it's unnecessary and counting on React to not change how HTML is rendered is sufficient?

All 20 comments

var rendered = wrapper.find('input');
expect(rendered.props()).to.have.property('maxLength', '10');

Maybe I am misunderstanding, but isn't 'maxLength' the React component prop? I'm looking for a way to test the rendered HTML. For example:

var rendered = wrapper.find('input');
expect(rendered.html().indexOf('maxlength="10"')).not.toEqual(-1);

Notice the capitalization difference as well.

I guess the test may not be so useful given a constant React API, but to be completely covered it's nice to be able to test the rendered HTML.

My stance is to never try and test the rendered HTML as that is effectively testing react and not your component. It is reacts job to pass the prop through to the HTML element. It's your components job to set the prop on the input.

So with that in mind, do you still want to test the HTML output?

I simultaneously agree and disagree. I agree that it's the component's job to set the prop on input, and React's job to pass the prop through to the HTML element.

However, a user is going to see the HTML, so that's really what I need to make sure continues to work over time, both as my codebase evolves, and as I update React to the latest version.

If not by testing the HTML output, how do you test for this? Or do you think that it's unnecessary and counting on React to not change how HTML is rendered is sufficient?

I understand your thought process completely. Seems totally valid. I personally feel comfortable counting on react to handle t for me. That's pretty core to their API so I doubt it would ever change, and if it did it would be a major breaking change that would require a manual update, at which point fixing tests is understandable.

Just my thoughts. But if you want to test it that's totally your prerogative.

Generally you want to do as tight a unit test as you can achieve without stubbing and mocking, on every level of your abstractions.

I'd say that you don't want to write integration tests that test React until you already have full test coverage (using shallow, most likely) of the actual code you've written.

Then, at that point - you don't actually care about the HTML, because the user doesn't see the HTML. The user sees the DOM, which the browser infers from the HTML. So if that's the kind of test you want, you _must_ do an actual in-browser integration test - like Selenium tests, for example. In other words, writing tests that check against the HTML doesn't give you the certainty you want, it just rewrites React's own tests, for little to no gain to you.

Point taken about the DOM being what the user sees. I would say that I do have very nearly full test coverage on the code I've actually written, and was looking for a full-proof way for a test to always fail when the user experience is broken.

I guess the reason I got to the point I was at with testing the rendered HTML is that testing the maxlength and other similar properties of the many input fields in my application seemed like overkill and would have caused slow Selenium tests.

However, you made me realize that it wouldn't have been necessary to test those properties for every input field (given that you have tested every input for props as shown above). This way you both test your code completely and still have a failing test if a change in React breaks you app.

That said, perhaps this issue is ill-conceived and could be closed. Although still possible if needed, perhaps we don't want to make it any easier to test the rendered HTML.

Yeah, I agree. That is not a feature we aim to support. I'm going to close this now. Thanks for your input!

Just in case someone stumbles upon this -- this recently added method gives you the DOMNode, so you should be able to test the rendered output https://github.com/airbnb/enzyme/blob/master/docs/api/ReactWrapper/getDOMNode.md

@blainekasten there are times you do want to test a value of an html attribute such as to assert that an src has a value once rendered to jsdom so I disagree you should not be testing for html attribute values.

example:

it('shows a company logo', () => {
            const src = "/lib/assets/some-logo.png",
                job = {company: {logo: src}},
                 imgSrc = mount(<Item job={job}/>).find('.job-list-item').find('img').prop('src');

            expect(imgSrc).to.equal(src);
        })

@dschinkel that's not testing the HTML value, that's testing the React prop value being passed to <img /> - which is a good thing to test.

@ljharb

testing the HTML value

What I mean is...testing the rendered DOM state(data) (prop value I sent in to be rendered to jsdom ends up what I expected it to be. I may still get no errors and could a problem somewhere still if the rendered data isn't what I expected). I do that buy checking a rendered element's attribute value, in this case img's src.

right - you're not exercising jsdom there, .prop() doesn't touch the DOM, it touches the react element that would end up being an img tag.

I'm not sure if the confusion (I am missing something fundamental) is on my side or yours (my language is confusing you), I simply meant testing that the src attribute on element after mounted - mount() to jsdom. But then you have a render() method for enzyme, can you explain the diff? I see it's a "CheerioWrapper" for render().

The fact that you've used mount to jsdom is irrelevant, .prop('src') is still not touching jsdom at all, it's extracting the prop from the react element in the tree.

When you use render, it's converted to a cheerio wrapper around HTML - so if you test src there, you're testing the actual HTML (and that's what you shouldn't bother testing).

Ok yea I see what you're saying. thx for the clarification.

@dschinkel if you use getDOMNode() you'll get the behavior you expect

@tonyjmnz ... unless it's a stateless functional component which isn't supported by getDOMNode().

I'm able to test HTML attributes using CSS attribute selectors, e.g.

expect(wrapper.exists("input[maxlength=10]")).toBe(true);
Was this page helpful?
0 / 5 - 0 ratings

Related issues

abe903 picture abe903  路  3Comments

modemuser picture modemuser  路  3Comments

blainekasten picture blainekasten  路  3Comments

AdamYahid picture AdamYahid  路  3Comments

benadamstyles picture benadamstyles  路  3Comments