简体   繁体   中英

Test unmounting with react-testing-library

I am confused by when/how a queried element get updated. For example:

I have a button that counts up. And when it counts to 3, it disappears.

import React, { useState } from "react";

export default () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      {count < 3 && (
        <button onClick={() => setCount(count + 1)}>{count}</button>
      )}
    </div>
  );
};

I'm testing it like so:

test("Counter counts up and disappears", () => {
  const { queryByText } = render(<App" />);

  const button = queryByText("0");
  expect(button.textContent).toEqual("0");

  fireEvent.click(button);
  expect(button.textContent).toEqual("1");

  fireEvent.click(button);
  expect(button.textContent).toEqual("2");

  fireEvent.click(button);
  expect(button).toBe(null);
});

The test failes with the following error:

expect(received).toBe(expected)

Expected value to be (using ===):
  null
Received:
  <button>2</button>

Why does the button element knows its text content has changed? We didn't need to run queryByText("1") and queryByText("2") after each click.

And why it doesn't know that the button has been unmounted after the last click? And we do need to do queryByText("3") if we want to check that it is indeed null.

Here's the code sandbox: https://codesandbox.io/s/react-testing-library-demo-76zhi

I guess it's about how react-dom and jsdom (which used by testing too) works.

(Please correct me or let me know if guessing is unacceptable! I will remove this. )

  1. Button type return by queryByText is HtmlElment

  2. react or react-dom is just minimize the task for dom manipulation, eventually jsdom would still do the text changing and button removing works.

  3. Since the testing tool is using jsdom , I check the removeChild part of code, and believe they are following W3C specification . I assume in the level of dom manipulation, jsdom would works like browsers. Thus I simulated your code like this:

 const btn = document.getElementById('btn'); const btnContainer = document.getElementById('btnContainer'); btn.textContent = '1'; console.log('btn after click 1: ' + btn); btn.textContent = '2'; console.log('btn after click 2: ' + btn); btnContainer.removeChild(btn); console.log('btn after removed: '+ btn); 
 <div id='btnContainer'><button id='btn'>0<button></div> 

  1. As you can see, after removing from div , the btn variable is still [object HTMLButtonElement] Maybe It's similar to use splice to an array which contained a defined object, the initial obj still remains the same?

 const obj = { 'a': 1}; const arr = [obj]; arr[0]['a'] = 2; arr.splice(0, -1); console.log(obj) 


Therefore, I think it's more about how garbage collection works in javascript .

In addition, maybe not to reuse the query result, and always query again in this situation, the result will be more expectable.


PS I found there is a method call toBeInTheDocument !

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM