• בלוג
  • עמוד 98
  • היום למדתי: הפונקציה rerender של react-testing-library

היום למדתי: הפונקציה rerender של react-testing-library

31/03/2022

הפונקציה rerender עושה בדיוק מה שהשם שלה רומז, ואני לא מאמין שהעברתי חיים שלמים בלעדיה. היא הכי שימושית כשיש לנו קומפוננה שה props שלה צריכים להשתנות במהלך הבדיקה. ככה זה עובד-

1. הקומפוננטה שאני בודק

בשביל המשחק ניקח קומפוננטה שמציגה מספר כלשהו של תיבות טקסט מסונכרנות, כשהמספר מתקבל דרך prop. אני רוצה לוודא שכשאני משנה את מספר התיבות, הטקסט בכל התיבות עדיין נשאר אותו דבר. זה קוד הקומפוננטה:

export function ManyInputs(props) {
  const { n=5 } = props;
  const [ value, setValue ] = useState('');

  return (
    <div data-testid="many-inputs">
      {new Array(n).fill(0).map((_, index) => (
        <input
          key={index}
          type="text"
          value={value}
          onChange={(e) => setValue(e.target.value)}
        />
      ))}
    </div>
  );
}

2. החיים בלי rerender

דרך מסורבלת לבדוק את ההתנהגות של הקומפוננטה כשמספר התיבות משתנה היא לשים את הקומפוננטה בקומפוננטת עזר שנכתוב בקוד הבדיקה. לחיצה על כפתור או שינוי טקסט בקומפוננטת העזר תשנה את מספר התיבות, וכך אפשר יהיה לראות את האפקט.

הנה בדיקה לדוגמה שמשתמשת ברעיון הזה:

test('change n', () => {
  const Helper = () => {
    const [n, setN] = useState(5);
    return (
      <div>
        <label>
          Number Of Boxes:
          <input type="text" value={n} onChange={(e) => setN(Number(e.target.value))} />
        </label>
        <ManyInputs n={n} />
      </div>
    );
  };

  render(<Helper />);

  let allInputs = within(screen.getByTestId('many-inputs')).getAllByRole('textbox');
  userEvent.type(allInputs[0], 'hello');
  for (const tb of allInputs) {
    expect(tb).toHaveDisplayValue('hello');
  }
  userEvent.type(screen.getByLabelText('Number Of Boxes:'), '8');

  allInputs = within(screen.getByTestId('many-inputs')).getAllByRole('textbox');
  for (const tb of allInputs) {
    expect(tb).toHaveDisplayValue('hello');
  }
});

3. החיים היפים עם rerender

במקום לבזבז מקום על כתיבת קומפוננטת עזר, הפונקציה rerender יכולה לעזור לנו לשנות prop על הקומפוננטה שאותה אנחנו בודקים. הפונקציה נשלחת אלינו כשדה באוביקט שחוזר מ render, ומספיק להפעיל אותה עם ה props החדשים. הנה קוד הבדיקה המעודכן:

test('change n', () => {
  const { rerender } = render(<ManyInputs n={5} />);

  let allInputs = within(screen.getByTestId('many-inputs')).getAllByRole('textbox');
  expect(allInputs).toHaveLength(5);
  userEvent.type(allInputs[0], 'hello');
  for (const tb of allInputs) {
    expect(tb).toHaveDisplayValue('hello');
  }
  rerender(<ManyInputs n={8} />);

  allInputs = within(screen.getByTestId('many-inputs')).getAllByRole('textbox');
  expect(allInputs).toHaveLength(8);
  for (const tb of allInputs) {
    expect(tb).toHaveDisplayValue('hello');
  }
});

יותר קצר, יותר מדויק והרבה יותר קל לתחזוקה.