Tips for Component Test Case Writing
If you have ever struggled writing your component test case, you’re not alone. After a few years in practice, I found some useful tips that help the process easier. I summerize them here, hope this would help you too. In this blog, I assume that we use Jest and React Testing Library, but the tips can be used for other testing libraries; the syntax may be different, though.
この記事の目次
- I. Why do we need to test component
- II. Tips for writing test case
- 1. Start every test with a simple display case
- 2. Be flexible in getting elements
- 3. What to do with “Unable to find an element” error
- 4. Expect a component that is not in the document
- 5. Test user events that affect value inside component
- 6. Test user events that affect value outside component
- 7. Test file extension
- 8. Mock module
I. Why do we need to test component
Like any other tests, the main point of component testing is to make sure your component function properly. We will not focus on how it’s display, but how it functions. Imagine writing test cases that match user interactions. The most difficult thing in component testing is getting the correct element on which you have to imitate user event. If you get the element correctly, the rest is not so hard. That’s why most of the tips below will mainly about how to get the element and what to do when tests return error that it’s unable to find the element.
II. Tips for writing test case
1. Start every test with a simple display case
For example, if I have to test a Button component, first I write a simple test case that renders the component and expects that component to exist. I know this test seems dumb, but keep reading; you will understand why.
After you run the test, check your test coverage folder and preview the HTML file that matches your test component.
This coverage file tells you how much of the component your test has covered. Your purpose is to make it 100% covered, which means your test has reached all parts of the component. If you see a yellow highlight, it hints where your test is missing; by that, you can add correspondent cases.
2. Be flexible in getting elements
If you cannot get the element you want using “screen.”. You can think of using a container. Querying with a container is similar to document query.
(document.getElementById, document.getElementsByClassName, etc.). Using container is like below:
const {container} = render(<Button>Click</Button>);
const element = container.getElementsByClassName("w-full");
3. What to do with “Unable to find an element” error
Most of the time, component can be complicated, with lots of element inside and you don’t have any specific information like id of name, etc. to find the element.
If you try tip #2 and still get this error. You can try to assume the element has a wrong classname or select a unique eleent nearby. For example:
const Component = () => { return ( <div className="w-full"> <p className="whitespace-pre ....complicated classname1">Blabla</p> <p className="whitespace-pre ....complicated classname2">...other nested and complicated HTML</p> <p className="whitespace-pre ....complicated classname3">...other nested and complicated HTML</p> <p className="whitespace-pre ....complicated classname4">Information</p> ...other nested and complicated HTML </div>) }
If I want to get that <p> element that has “Information” text, but there is no special trait of that element, I can think of getting the <div> outside and work my way into the element I want.
const {container} = render(<Component/>); const outerElement = container.getElementByClassName("w-full"); const pElement = outerElement?.children[3]; expect(pElement.content).toContain("Information");
If your expectation is failed, but the error is no longer “Unable to find an element”, you can expect the element to have wrong className.
const {container} = render(<Component/>); const outerElement = container.getElementByClassName("w-full"); const pElement = outerElement?.children[3]; expect(pElement.className).toBe("w-full"); // this is wrong
The reason to do this, is that, when you run this test, your test is definitely failed, BUT, the console will show something like this:
Expected: "w-full"; Received: "whitespace-pre ....complicated classname3"
Then, you can see that you’re 1 element away from the correct element you want to get, now you can adjust you test case to
const pElement = outerElement?.children[4]; instead of const pElement = outerElement?.children[3];
4. Expect a component that is not in the document
If you want to expect an element that is not exist in the document. Use “.queryBy” instead of “.getBy”.
Then you can expect(element).not.toBeInTheDocument().
If you get an element using “.getBy”, it will throw an error saying that there is no element to get.
5. Test user events that affect value inside component
If you want to test the result of a userEvent, and that result happens inside a component, sometimes you need to use “await waitFor(() => { })”.
Hints: If you use userEvent in the test code and the test failed with an error (say that you were unable to find an element), it’s properly the right time to think of waitFor.
6. Test user events that affect value outside component
If you want to test result of a userEvent, and that event update a value that is passed to component by props. Write a TestComponent and test it.
As below example, I try to test if I select a date in my calendar, will the date be as I expected. Since selected date is passed from outside, when I select a date, it should be update outside.
7. Test file extension
There are some cases, when your test expectation requires the test file to be in .tsx in other to work. So please keep this in mind.
If you want to add TestComponent inside your test file, or you want to use the assertion “.toBeInTheDocument()”, your test file should be in .tsx
8. Mock module
If component render is based on the result of an API, definitely use mock. There are 2 types of mocking a module: mocking a whole module or just a part of it.
- Either way, jest.mock should be written right below import, otherwise it won’t work.
- If you mock an API, mock data should be in form of a response, not just the data you need.
Like this, for example:
mockData = { data: { items: [] } } , not mockData = { items: [] }
That’s a wrap! This might seem little, but it really helps me through the testing phase. It, of course, still requires your own adaptation, since every component is different, and these are just some tips. Anyways, I hope this helps, no matter how big or small.
カテゴリー: