Enzyme: Assertion based on attribute or get attribute from element.

Created on 9 Nov 2016  路  17Comments  路  Source: enzymejs/enzyme

Hi I am testing a image component and I tried to verify its src attribute, I couldn't find any option to assert the attribute or get the value of attribute, my workaround is:

    expect(component.find('img').html()).toContain(IMAGE_PATH);

It works but I really prefer to have a way to assert or get the attribute:

    expect(component.find('img').attr('src')).toEqual(IMAGE_PATH);
    expect(component.find('img')).hasAttr('src', IMAGE_PATH);
question

Most helpful comment

Ohhh @ljharb it works thanks. I don't require a filter, it works with prop, because I only have one image :)

    expect(login.find('img').prop('src')).toEqual(IMAGE_PATH);

All 17 comments

expect(component.find('img').filterWhere({ src: IMAGE_PATH })).to.have.lengthOf(1)?

Hi @ljharb I tried the line but I have the error TypeError: predicate is not a function
I review the docs about _filterWhere_, in this case the item is a ReactWrapper instance and I don't know how to get an attribute from it.

    component.find('img').filterWhere((item) => {
      return item.src === IMAGE_PATH; //src undefined
    });

@ilacyero oops, right:

expect(component.find('img').filterWhere((item) => {
  return item.prop('src') === IMAGE_PATH;
})).to.have.lengthOf(1);

Ohhh @ljharb it works thanks. I don't require a filter, it works with prop, because I only have one image :)

    expect(login.find('img').prop('src')).toEqual(IMAGE_PATH);

I don't think .prop does what you expect.

It does not appear to assert that a particular HTML property was emitted. It just asserts that the prop was passed to that element.

Consider this case, which looks like it should succeed, but which actually fails:

// component implementation
const MyComp = ({ id , ...props }) => {
    // pretend I do some interesting logic based on the ID
    // but I should also emit the ID; for the sake of example, I forget to actually emit it
    return (<div {...props}> me </div>)
}

// test
const comp = mount( <MyComp id="thing" /> )
expect( comp.prop('id') )
.toBe( 'thing' )

The test will pass, because MyComp was fed id="thing". However, if your testing goal is to ensure that the component successfully emitted that ID on the element, you should expect a failure.

I think .prop is _not_ the thing you are looking for. I'm still looking.

+1 for this. In my case I want to test that my <Form /> component outputs an HTML form with method="post" if no props are passed to the component. Using .prop does not allow me to test this.

Any advice on how to proceed would be much appreciated.

Thanks 馃槃

@opr expect(shallow(<Form />)).props()).to.have.property('method', 'post');?

@ljharb thanks for that but it doesn't work. I don't think this returns the element that gets rendered.

I managed to solve it using findDOMNode from react-dom like this.

const myForm = mount(<Form />);
expect(findDOMNode(myForm.instance()).querySelector('form').getAttribute('method')).to.equal('get');

Hope this helps others in future, thanks for all the amazing work you're doing. 馃嵒

Note that I'm suggesting using shallow. mount indeed does not return the thing being rendered, it returns the root component itself.

I have the solution I think many of you are looking for.

tl;dr:

// inside a unit test
const dom = Enzyme.mount( <MyComp id="example" className="test_class" /> )

const outermost = dom.childAt(0) // this is key!

// verify that "test_class" appears on rendered DOM
expect( outermost.hasClass( 'test_class' ) )
.toBe( true )
//=> success

Enzyme.mount returns a "ReactWrapper". This is probably not what you think it is.

Imagine this is my component implementation:

const MyComp = ({ className, ...props }) => {
  return (
    <div aria-role="whatever" className={`MyComponent ${className}`} {...props}>
      { /* stuff */ }
    </div>
  )
}

Here is what we expect to be written to the DOM, but this is not whatEnzyme.mount returns:

<div aria-role="whatever" className="MyComponent test_class" id="example">
  ...
</div>

Instead, it returns something like this:

<MyComp id="example" className="test_class">
  <div aria-role="whatever" className="MyComponent test_class" id="example">
    // stuff
  </div>
</MyComp>

The invented Component tag is there, wrapping the actual render output of the component! WTF?! This is not what gets rendered in a browser, but it apparently _is_ what gets sent to the React rendering engine, at which point (presumably) the bs <MyComp> tag boils out, leaving the mundane <div>.

This is consistent with the behavior I've observed, and that I think others have been frustrated by: inspecting the .props() of Enzyne.mount's return does not tell you whether the component implementation emits the right stuff on its outermost element, it tells you whether the test component was given the props you expect -- which is probably trivially true.

Aside: by inspecting the intermediate representation, Enzyme avoids turning our unit tests into full-on integration tests of the React renderer. This is a good thing. This confusion is the only downside.

Most Enzyme example code I've seen uses .find('tagname'), which frustrates learning this stuff because it obscures the fact that the result of your render method is _a child_ of mount's return value.

So, the facts:

  • you _can_ reliably inspect the HTML attributes that appear on the outermost element of your component, using .props(), but that outermost element will be _the first child_ of the "node" that is returned by Enzyme.mount
  • the value of style will be a React-style object, not a string of CSS statements
  • there are probably other little differences between the ReactWrapper representation and what would be written to the DOM; when in doubt, try something like this: expect(dom.debug()).toBe(false) -- it will cause the ReactWrapper content to be logged to the console, and you can then get a look at the details

I hope this helps. This was kind of an RTFM moment for me. At the same time, I think Enzyme's documentation is subtly misleading, so let's all let ourselves off the hook and pat ourselves on the back for even bothering to write unit tests.

Consider a simple component like this:

interface OwnProps {
  title: string;
}

class Button extends React.Component<OwnProps> {
  public render() {
    return (
      <input
        type="button"
        value={this.props.title}
      />
    );
  }
}

export default Button;

And then what I do in the test is

    const component = mount(<Button title="Hello world"/>);
    expect(component.getDOMNode().attributes.getNamedItem('value').value)
      .toEqual('Hello world');

Cheers!

@krzysztofbukowski I think the point is to get the tests running without using shallow()
(though, I might be wrong)

The ideal is to primarily use shallow; mount/render should only be used when necessary.

You can also find by attributes, using the attribute selector syntax and then extract the text.

EX)
<img src='foo' />
expect(component.find('img').find('[src]').props().src).toEqual('foo'); // passes

I've created an extension for the expect method of Jest with toHaveAttributeValue and it does the trick.

I did this:

expectExtend.js

const extensions = {
    toHaveAttributeValue: (element, attributeName, valueToVerify) => {
        const actualValue = element.getAttribute(attributeName);
        const pass = actualValue === valueToVerify;
        return pass ? {
            message: () => `${element.tagName} has attribute "${attributeName}" set to "${valueToVerify}"`,
            pass: true
        } : {
            message: () => `expected ${element.tagName} to have attribute "${attributeName}" set to "${valueToVerify}", received "${actualValue}"`,
            pass: false
        }
    }
};

export default (expect) => {
    expect.extend(extensions);
};

myTestFile.test.js

import expectExtend from "./lib/expectExtend";

expectExtend(expect);

test("Something that needs to test the src attribute", () => {
    img.setAttribute("data-src", img200);
    img.setAttribute("data-srcset", img400);
    setSources(img, lazyloadSettings);
    expect(img).toHaveAttributeValue("src", img200);
    expect(img).toHaveAttributeValue("srcset", img400);
});

What do you think?

.find is able to use "jquery selector" syntax:

 expect(component.find('img [src="myImage"])).toHaveLength(1);

i used:

const locName= mountedButton.find('.location-button');
expect(locName.getElements()[0].props.value).toEqual('All Locations');

to get the value property of an element. (Jest/enzyme)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

andrewhl picture andrewhl  路  3Comments

abe903 picture abe903  路  3Comments

modemuser picture modemuser  路  3Comments

ivanbtrujillo picture ivanbtrujillo  路  3Comments

potapovDim picture potapovDim  路  3Comments