I am implementing React testing library as follows:
const onCardClick = jest.fn();
afterEach(cleanup);
describe('Card', () => {
test('renders quote card and allows click', () => {
const { getByTestId } = render(<Card onClick={onCardClick} />);
fireEvent.click(getByTestId('card-root'));
expect(onCardClick.mock.calls.length).toBe(1);
});
test('renders card passes index on click', () => {
const { getByTestId } = render(
<Card onClick={onCardClick} index={2} />
);
fireEvent.click(getByTestId('card-root'));
expect(onCardClick.mock.calls.length).toBe(1);
expect(onCardClick.mock.calls[0][0]).toBe(2);
});
});
Could there be a way that both test cases are not dependent on each other, as currently when first is run the test is passed as it is clicked one time, when second test is run now click is expected as 2(It should be expected as 1 only as a separate click) . I want these test cases not to be dependent on each other.
Related
I'm trying to figure out why my test - which passes when ran alone - is failing whenever the describe block contains more than 1 test. Take this example, which I've taken from my real code and simplified:
describe('Create Account Form', () => {
const {container} = render(<CreateAccountForm />);
const email = container.querySelector('input[name="email"]');
const password1 = container.querySelector('input[name="password1"]');
it('Should render all fields', () => {
allInputs.forEach((input) => {
expect(input).toBeInTheDocument();
});
});
it('Another test', () => {
expect(email).toBeInTheDocument(); // fails
});
});
The 2nd test fails, but passes only when commenting out the first test, or re-rendering the container again in the test like this:
it('Another test', () => {
const {container} = render(<CreateAccountForm />);
const email = container.querySelector('input[name="email"]');
expect(email).toBeInTheDocument(); // passes
});
Why does this have to happen? I would much rather not have to re-render the container and declare new variables inside each test block.
Thank you
RTL will unmount React trees that were mounted with render in afterEach hook. See cleanup.
Please note that this is done automatically if the testing framework you're using supports the afterEach global and it is injected to your testing environment (like mocha, Jest, and Jasmine).
Move the render code into beforeEach or individual test case. So that we can create react trees before each test case. Isolate test cases from each other, using their own test data without affecting the rest.
E.g.
index.tsx:
import React from 'react';
export function Example() {
return (
<div>
<input name="email" />
<input name="password1" />
</div>
);
}
index.test.tsx:
import { render } from '#testing-library/react';
import '#testing-library/jest-dom/extend-expect';
import React from 'react';
import { Example } from './';
describe('70753645', () => {
let email, password1, allInputs;
beforeEach(() => {
const { container } = render(<Example />);
email = container.querySelector('input[name="email"]');
password1 = container.querySelector('input[name="password1"]');
allInputs = container.querySelectorAll('input');
});
it('Should render all fields', () => {
allInputs.forEach((input) => {
expect(input).toBeInTheDocument();
});
});
it('Another test', () => {
expect(email).toBeInTheDocument();
});
});
Test result:
PASS stackoverflow/70753645/index.test.tsx (9.222 s)
70753645
✓ Should render all fields (24 ms)
✓ Another test (3 ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 9.717 s
package versions:
"#testing-library/react": "^11.2.2",
"jest": "^26.6.3",
I am trying to write some test cases using jest and enzyme,I am unable to add test cases for Link and Please suggest if any more test cases needs to be added
these is styled component, and when should we spy /mock function
displayErr.tsx
export const DisplayErr = React.memo<Props>(({ text, SearchLink }) => (
<Section>
<h1>{text}</h1>
{!SearchLink && <Link to={getPath('SEARCH')}>some text</Link>}
</Section>
));
displayErr.test.tsx
describe('Error Msg', () => {
it('styled component', () => {
const output = mount(<DisplayErr text="hello world" SearchLink={true} />);
const link = output.find(Link).find({ to: '/Search' });
expect(output.find(Section).text()).toEqual('hello world');
//expect(link).toBe('<div class="link">Login</div>');//error
});
});
Here Link will render based on the SearchLink props. As you are passing it as true, it wont be available in the DOM. Pass SearchLink=false and execute the unit tests.
describe('Error Msg', () => {
it('styled component', () => {
const output = mount(<DisplayErr text="hello world" SearchLink=false/>)
expect(output.find(Link).props().to).toEqual('/search');
});
Let me know if you are facing the same issue.
Code structure is as same as given below:
FunctionComponent.js
...
const [open, handler] = useState(false);
setTimeout(() => {handler(true);}, 2000);
...
return (
...
<div className={active ? 'open' : 'close'}>
)
comp.test.js
jest.useFakeTimers();
test('test case 1', () => {
expect(wrapper.find('open').length).toBe(0);
jest.advanceTimersByTime(2000);
expect(wrapper.find('open').length).toBe(1);
jest.useRealTimers();
});
The problem is that the expression written in bold in test is saying the length of open class is still 0, so actual and expected are not meeting.
You want to test the outcome of the hook and not the hook itself since that would be like testing React. You effectively want a test where you check for if the open class exists and then doesn't exist (or vice versa), which it looks like you're trying.
In short, to solve your issue you need to use ".open" when selecting the class. I would also suggest using the .exists() check on the class instead of ".length()" and then you can use ".toBeTruthy()" as well.
You could look into improve writing your tests in a Jest/Enzyme combined format as well:
import { shallow } from 'enzyme';
import { FunctionComponent } from './FunctionComponent.jsx';
jest.useFakeTimers();
describe('<FunctionCompnent />', () => {
const mockProps = { prop1: mockProp1, prop2: mockProp2, funcProp3: jest.fn() };
const wrapper = shallow(<FunctionComponent {...mockProps} />);
afterEach(() => {
jest.advanceTimersByTime(2000);
});
afterAll(() => {
jest.useRealTimers();
});
it('should render as closed initially', () => {
expect(wrapper.find('.close').exists()).toBeTruthy();
// you could also add the check for falsy of open if you wanted
// expect(wrapper.find('.open').exists()).toBeFalsy();
});
it('should change to open after 2 seconds (or more)', () => {
expect(wrapper.find('.open').exists()).toBeTruthy();
// you could also add the check for falsy of close if you wanted
// expect(wrapper.find('.close').exists()).toBeFalsy();
});
});
EDIT: Sorry realised I wrote the test backwards after checking your code again, they should be fixed now.
I have imported a table component in a different component file and I am passing props form the parent component.
TableWrapper.js
const handleRowClick = rowData => {
// function data
}
<TableRender onRowClick={handleRowClick} id={'AUDIT'} />
I am writing test cases for this kind of function as I want this function to be covered and pass the data to it rowData
testFile.js
import React from 'react';
import { shallow } from 'enzyme';
it('handle row click is called', () => {
const handleRowClick = jest.fn();
const wrapper = shallow(<TableWrapper {...props} onRowClick={handleRowClick} />);
const rowClickFunction = wrapper.find('.ra--audit-table__content');
rowClickFunction.simulate('handleRowClick');
expect(handleRowClick).toBeTruthy();
})
If I do this then it passes the test case but does not cover the function in coverage.
testFile.js
import React from 'react';
import { shallow } from 'enzyme';
it('handle row click is called', () => {
const handleRowClick = jest.fn();
const wrapper = shallow(<TableWrapper {...props} onRowClick={handleRowClick} />);
const rowClickFunction = wrapper.find('.ra--audit-table__content');
rowClickFunction.simulate('handleRowClick');
expect(rowClick).toHaveBeenCalledTimes(1);
})
If I do this change it gives me an error:-
expect(jest.fn()).toHaveBeenCalledTimes(expected)
Expected number of calls: 1
Received number of calls: 0
I want this function to b covered.
Any Idea for this?
you are simulating wrong event. you should simulate click.
change:
const rowClickFunction = wrapper.find('.ra--audit-table__content');
rowClickFunction.simulate('handleRowClick');
to:
const elem = wrapper.find('.ra--audit-table__content');
elem.simulate('click');
(also make sure the clickable element has a .ra--audit-table__content class)
you are creating wrong component. you should create a TableRender, not TableWrapper.
change:
const wrapper = shallow(<TableWrapper {...props} onRowClick={handleRowClick} />);
to:
const wrapper = shallow(<TableRender onRowClick={handleRowClick} />);
Code (testFile.js) :
it('handle row click is called', () => {
const handleRowClick = jest.fn((i) => {console.log(`row ${i} clicked`)});
const wrapper = shallow(<TableRender onRowClick={handleRowClick} />);
const elem = wrapper.find('.ra--audit-table__content');
elem.simulate('click');
// expect(handleRowClick).toBeTruthy();
expect(handleRowClick).toHaveBeenCalledTimes(1);
})
Ps: i tested it and it works fine.
I'm learning about unit testing React components using react-testing-library
I have the component rendering correctly, however, when I aim to break the test into smaller chunks inside a describe() function. The test breaks and here's why.
Current only one or the other test() passes but not both
import React from 'react'
import 'react-testing-library/cleanup-after-each'
import { render, fireEvent } from 'react-testing-library'
import Quantity from '../components/Quantity'
describe('Quantity Component', () => {
const { container, getByTestId } = render(<Quantity />)
// first test
test('checks that quantity is never 0', () => {
expect(getByTestId('quantity')).not.toBe('0')
})
// second test
test('checks for the initial product quantity count', () => {
expect(getByTestId('quantity')).toHaveTextContent('1')
fireEvent.click(getByTestId('increment'))
expect(getByTestId('quantity')).toHaveTextContent('2')
})
})
When trying to run both tests it errors:
Unable to find an element by: [data-testid="quantity"]
[data-testid="quantity"] is just an attribute that I passed inside my desired JSX tag.
The test passes when running only the first or second test but not both concurrently.
What am I missing here?
Cross-contamination is strictly discouraged in unit testing.
The problem is that a setup occurs only once per Quantity Component suite, while it should be done for each test. This is what beforeEach is for:
describe('Quantity Component', () => {
let container, getByTestId;
beforeEach(() => {
({ container, getByTestId } = render(<Quantity />));
});
...
You need to also use an afterEach cleanup.
describe('your tests', () => {
afterEach(cleanup);
beforeEach(() => ({container, getById} = render(<Quantity />))
it('does something', () => {
expect(getByTestId('quantity')).toHaveTextContent(0);
}
}
I suggest you call the render inside your it clauses, it keeps the tests easier to manage:
describe('Quantity Component', () => {
test('checks that quantity is never 0', () => {
const { container, getByTestId } = render(<Quantity />)
expect(getByTestId('quantity')).not.toBe('0')
})
test('checks for the initial product quantity count', () => {
const { container, getByTestId } = render(<Quantity />)
expect(getByTestId('quantity')).toHaveTextContent('1')
fireEvent.click(getByTestId('increment'))
expect(getByTestId('quantity')).toHaveTextContent('2')
})
})
The added advantage is that if for some reason one of your tests needs to run with different props you can do that more easily with this setup.