Josh Desmond
Josh Desmond

Reputation: 690

Can I, (and should I), make assertions about what HTML elements a component renders using Enzyme's shallow()?

The question:

I am writing a unit test for a React component called <BaseEdit />. In order to test that the component works, I want to assert that it will render an HTML element <input/>, and I want to do so using shallow() instead of mount().

The following test case works:

const baseEditWrapper = mount(<BaseEdit />)
const inputElement = baseEditWrapper.find('input')
assert(inputElement.exists())
assert.strictEqual(inputElement.prop('type'), 'text')

However, if I change mount() to shallow(), then the test fails. It fails because baseEditWrapper.find('input') returns an empty/stub/nonexistant ShallowWrapper object.


The Underlying HTML:

The <BaseEdit /> component, when mounted using Enzyme and JSDom, creates the following DOM elements.

<div class="MuiFormControl-root MuiTextField-root WithStyles(ForwardRef(TextField))-root-27 WithStyles(ForwardRef(TextField))-root-96" style="width:120px">
  <div class="MuiInputBase-root MuiInput-root MuiInput-underline MuiInputBase-formControl MuiInput-formControl">
    <input type="text" aria-invalid="false" class="MuiInputBase-input MuiInput-input"/>
  </div>
</div>

Extra Details:

The documentation for shallow() is here.

The question, when should you use render and shallow in enzyme react tests?, shows that finding by selectors is supported when using shallow().

The test case below proves that shallow does rendering multiple layers of HTML elements, and acts as a work around solution for my question.

const shallowWrapper = shallow(<BaseEdit />)
assert(shallowWrapper.html().includes('<input')
assert(shallowWrapper.html().includes('type=\"text\"'))

Such a solution seems hacky, however, and I would rather keep my solution in line with the use of Enzyme's ShallowWrapper interface.

Upvotes: 1

Views: 637

Answers (1)

helloitsjoe
helloitsjoe

Reputation: 6529

You should be able to use .dive() to find the input:

const input = wrapper.find(TextField).dive().find('input')

You might even need to dive more than once, if the input is nested inside multiple React components:

// This is a little contrived, but something like this:
const InnerTextField = () => <input />;
const TextField = () => <InnerTextField />;
const BaseEdit = () => <TextField />;

// You would need to do this to find the input:
const input = wrapper.find(TextField).dive().dive().find('input');

You also asked if you should use shallow:

If you want to make assertions on the underlying HTML (for example, you want to make sure BaseEdit always renders an input), you're probably better off using mount, unless there's a reason not to.

shallow is useful when you want to test a single layer of your component tree. It looks like you're using Material UI, so a possible test might be to check that BaseEdit is passing props correctly to TextField.

it('passes the required prop to the underlying component', () => {
  const wrapper = shallow(<BaseEdit required />);
  expect(wrapper.find(TextEdit).props().required).toBe(true);
  wrapper.setProps({ required: false });
  expect(wrapper.find(TextEdit).props().required).toBe(false);
})

Upvotes: 2

Related Questions