0

I was creating a component that returns a label and a children, this child is a function that evaluates if the field has type 'input' or 'textarea' and returns it:

export const Field = ({
  fieldType,
}) => {
  return (
    <>
      <label htmlFor={name}> {label}</label>
      {() => {
        switch (fieldType) {
          case 'textarea':
            return (
              <textarea
              />
            );
          default:
            return (
              <input/>
            );
        }
      }}
    </>
  );
};

I like to start my test by creating a snapshot of the component

describe('Unit testing: <Field /> component', () => {
  test('Should render correctly ', () => {
    const wrapper = shallow(<Field fieldType='textarea' />);

    expect(wrapper).toMatchSnapshot();
  });
});

This is the result of my snapshot (I'm using enzyme-to-json):

exports[`Unit testing for Field component Should render correctly  1`] = `
<Fragment>
  <label
    htmlFor="testField"
  >
     
    Test Label
  </label>
  <Component />
</Fragment>
`;

As you can see, the child has been rendered just as and this is very fuzzy to me... I would like to know how can I exactly test that my component is really rendering either an input or a textarea...

skyboyer
  • 22,209
  • 7
  • 57
  • 64
  • My snapshot shows a and I would like to know what can I do to test if my is returning either an input or a textarea! – Danilo Peña Aug 09 '20 at 13:34
  • When you use `shallow`, you are explicitly saying that you are not interested in whatever subcomponents of the component you are testing. Try with `mount` instead. – JulienD Aug 09 '20 at 14:00
  • It may be awkward to test because it's awkward to use, too. A good thing about tests is that they reveal problems in tested code. Why does it have function child in the first place? Function children are usable in React but not in places where they will be provided to a renderer. – Estus Flask Aug 09 '20 at 22:17

2 Answers2

0

I've found a possible solution that actually it's good for me:

const innerWrapper = shallow(wrapper.prop('children')[1]());

This innerWrapper creates a shallow render from the children. The snapshot shows what I wanted:

exports[`Unit testing for Field component Function as children should render correctly 1`] = `
<textarea
  autoComplete="off"
  id="testField"
  name="testField"
  value=""
/>
`;

The complete test that I've implemented:

test('Function as children should render correctly', () => {
    const innerWrapper = shallow(wrapper.prop('children')[1]());
    expect(innerWrapper).toMatchSnapshot();
    expect(innerWrapper.find(props.fieldType).exists()).toBe(true);
  });

And yes, I've ran the test and it passed.

  • Your component does not work. and you modified the test to ignore it. see my answer. – yaya Aug 11 '20 at 13:50
0

You mentioned in your answer to your question :

I've found a possible solution that actually it's good for me:

But it's a wrong solution. You have a wrong component, and you changed your test to ignore it. your component is like:

export const Field = ({fieldType,}) => {
  return (
    <>
      <label htmlFor={name}> {label}</label>
      {() => {return <input />}} <---- it's just a component defination.
    </>
  );
};

And if you use it like:

<Field />

It will only render label, not the textarea nor the input. (Because a function inside the render function is considered as a component definition, you should call it in order to get an element from it to render.)

So the test was correct, but your component is wrong. Change your component to:

export const Field = ({fieldType,}) => {
  const input = () => {
    return <input />
  }
  return (
    <>
      <label htmlFor={name}> {label}</label>
      {input()}
    </>
  );
};

To render the input component, not just defining it.

yaya
  • 7,675
  • 1
  • 39
  • 38
  • it is correct to use an IIFE to call the function that Switches either input or textarea? just like { (() => {switch(fieldType) ... })() } – Danilo Peña Aug 13 '20 at 03:57
  • 1
    @DaniloPeña that works but that's a bad practice since react will create a new function on each render (however personally i think it's ok and the performance improvement is not that huge): https://stackoverflow.com/questions/41369296/react-functions-inside-render so that's why i moved it outside render function. – yaya Aug 13 '20 at 08:17
  • @DaniloPeña (also note that in your original question, you didn't put an IIFE on render, you put the defination instead. just in case you didn't noticed it.) – yaya Aug 13 '20 at 08:51