简体   繁体   中英

How do you test for the non-existence of an element using jest and react-testing-library?

I have a component library that I'm writing unit tests for using Jest and react-testing-library. Based on certain props or events I want to verify that certain elements aren't being rendered.

getByText , getByTestId , etc throw and error in react-testing-library if the element isn't found causing the test to fail before the expect function fires.

How do you test for something not existing in jest using react-testing-library?

From DOM Testing-library Docs - Appearance and Disappearance

Asserting elements are not present

The standard getBy methods throw an error when they can't find an element, so if you want to make an assertion that an element is not present in the DOM, you can use queryBy APIs instead:

 const submitButton = screen.queryByText('submit') expect(submitButton).toBeNull() // it doesn't exist

The queryAll APIs version return an array of matching nodes. The length of the array can be useful for assertions after elements are added or removed from the DOM.

 const submitButtons = screen.queryAllByText('submit') expect(submitButtons).toHaveLength(2) // expect 2 elements

not.toBeInTheDocument

The jest-dom utility library provides the .toBeInTheDocument() matcher, which can be used to assert that an element is in the body of the document, or not. This can be more meaningful than asserting a query result is null .

 import '@testing-library/jest-dom/extend-expect' // use `queryBy` to avoid throwing an error with `getBy` const submitButton = screen.queryByText('submit') expect(submitButton).not.toBeInTheDocument()

Use queryBy / queryAllBy .

As you say, getBy* and getAllBy* throw an error if nothing is found.

However, the equivalent methods queryBy* and queryAllBy* instead return null or [] :

queryBy

queryBy* queries return the first matching node for a query, and return null if no elements match. This is useful for asserting an element that is not present. This throws if more than one match is found (use queryAllBy instead).

queryAllBy queryAllBy* queries return an array of all matching nodes for a query, and return an empty array ( [] ) if no elements match.

https://testing-library.com/docs/dom-testing-library/api-queries#queryby

So for the specific two you mentioned, you'd instead use queryByText and queryByTestId , but these work for all queries, not just those two.

You have to use queryByTestId instead of getByTestId.

Here a code example where i want to test if the component with "car" id isn't existing.

 describe('And there is no car', () => {
  it('Should not display car mark', () => {
    const props = {
      ...defaultProps,
      base: null,
    }
    const { queryByTestId } = render(
      <IntlProvider locale="fr" messages={fr}>
        <CarContainer{...props} />
      </IntlProvider>,
    );
    expect(queryByTestId(/car/)).toBeNull();
  });
});

getBy* 在找不到元素时抛出错误,因此您可以检查它

expect(() => getByText('your text')).toThrow('Unable to find an element');

Hope this will be helpfull

this table shows why/when function errors

which functions are asynchronous

what is return statement for function

在此处输入图像描述

You can use react-native-testing-library "getAllByType" and then check to see if the component is null. Has the advantage of not having to set TestID, also should work with third party components

 it('should contain Customer component', () => {
    const component = render(<Details/>);
    const customerComponent = component.getAllByType(Customer);
    expect(customerComponent).not.toBeNull();
  });

Another solution: you could also use a try/catch block

expect.assertions(1)
try {
    // if the element is found, the following expect will fail the test
    expect(getByTestId('your-test-id')).not.toBeVisible();
} catch (error) {
    // otherwise, the expect will throw, and the following expect will pass the test
    expect(true).toBeTruthy();
}

I recently wrote a method to check visibility of element for a jest cucumber project.

Hope it is useful.

public async checknotVisibility(page:Page,location:string) :Promise<void> 
{
    const element = await page.waitForSelector(location);
    expect(element).not.toBe(location); 
}
// check if modal can be open
const openModalBtn = await screen.findByTestId("open-modal-btn");
fireEvent.click(openModalBtn);

expect(
  await screen.findByTestId(`title-modal`)
).toBeInTheDocument();


// check if modal can be close
const closeModalBtn = await screen.findByTestId(
  "close-modal-btn"
);
fireEvent.click(closeModalBtn);

const sleep = (ms: number) => {
  return new Promise((resolve) => setTimeout(resolve, ms));
};

await sleep(500);
expect(screen.queryByTestId("title-modal")).toBeNull();
const submitButton = screen.queryByText('submit')
expect(submitButton).toBeNull() // it doesn't exist

expect(submitButton).not.toBeNull() // it exist

为我工作(如果你想使用 getByTestId):

expect(() => getByTestId('time-label')).toThrow()

don't want to bury the lead, so here's the right solution ✅

waitFor(() => queryByTestId(/car/) === null)

There are issues with all of the answers here so far...

don't use getByTestId , that'll have to wait for the timeout because it's expecting the element to eventually be there. Then it'll throw and you'll have to catch that, which is a less readable test. Finally you could have a RACE CONDITION where getByTestId is evaluated before the element disappears and our test will flake.

Just using queryByTestId without waitFor is a problem if the page is changing at all and the element has not disappeared yet. RACE CONDITION

deleteCarButton.click();
expect(queryByTestId(/car/)).toBeNull(); //

if expect() gets evaluated before the click handler and render completes we'll have a bad time.

The default behavior of queryByRole is to find exactly one element. If not, it throws an error. So if you catch an error, this means the current query finds 0 element

expect(
   ()=>screen.getByRole('button')
).toThrow()

getByRole returns 'null', if it does not find anthing

 expect(screen.queryByRole('button')).toEqual((null))

findByRole runs asynchronously, so it returns a Promise . If it does not find an element, it rejects the promise. If you are using this, you need to run async callback

test("testing", async () => {
  let nonExist = false;
  try {
    await screen.findByRole("button");
  } catch (error) {
    nonExist = true;
  }
  expect(nonExist).toEqual(true);
});
    

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