Hi I have a simple component I need to test:
MyComponent.js-----
import React from 'react';
const MyComponent = (props) => {
onClickHandler = () => {
console.log('clicked');
props.outsideClickHandler();
}
return (
<div>
<span className='some-button' onClick={onClickHandler}></span>
</div>
);
}
MyComponent.test.js----
import React from 'react';
import { shallow } from 'enzyme';
import MyComponent from './MyComponent';
describe('MyComponent', () => {
const onClickHandler = jest.fn();
it('calls click event', () => {
const wrapper = shallow(<MyComponent />);
wrapper.find('.some-button').simulate('click');
expect(onClickHandler.mock.calls.length).toEqual(1); // tried this first
expect(onClickHandler).toBeCalled(); // tried this next
});
});
Tried above two types of expect, my console log value is coming
console.log('clicked'); comes
but my test fails and I get this:
expect(received).toEqual(expected) // deep equality
Expected: 1
Received: 0
So, the problem with your code is when you simulate a click event, you expect a totally independent mock function to be called. You need to attach the mock function to the component. The best way is using prototype. Like this:
it('calls click event', () => {
MyComponent.prototype.onClickHandler = onClickHandler; // <-- add this line
const wrapper = shallow(<MyComponent />);
wrapper.find('.some-button').simulate('click');
expect(onClickHandler.mock.calls.length).toEqual(1);
expect(onClickHandler).toBeCalled();
expect(onClickHandler).toHaveBeenCalledTimes(1); // <-- try this as well
});
Refer to this issue for more potential solutions.
Related
I've been trying to test if a React function has been called on Click but i always get the same result :
Expected number of calls: >= 1
Received number of calls: 0
And the test fails, this is my Setup,
This is MyComponent
import React from 'react';
export default function MyComponent() {
function handleClick() {
console.log('Clicking');
}
return (
<div>
<button onClick={handleClick}>Click</button>
</div>
);
}
And here is my Jest Test
import { render, screen, fireEvent } from '#testing-library/react';
import MyComponent from './MyComponent';
describe('It should run the tests', () => {
it('Should call the funct', () => {
//Arrange
render(<MyComponent />);
const handleClick = jest.fn();
const button = screen.getByRole('button');
//Action
fireEvent.click(button);
//Assert
expect(handleClick).toHaveBeenCalled();
});
});
This works normally on the app, it does call the function and logs 'Clicking'
Thanks in advance!
You have defined the mock function but you're Component doesn't know anything about it since it's defined within the component.
What you could do is pass the onClick as a prop to the Component. This way you could pass any handler function required for the Component as a prop.
import React from 'react';
export default function MyComponent({ onClick }) { // 👈 receive as a prop
/* function handleClick() {
console.log('Clicking');
} */
return (
<div>
<button onClick={onClick}>Click</button> {/* 👈 call the prop */}
</div>
);
}
Update test:
import { render, screen, fireEvent } from '#testing-library/react';
import MyComponent from './MyComponent';
describe('It should run the tests', () => {
it('Should call the funct', () => {
//Arrange
const handleClick = jest.fn();
render(<MyComponent onClick={handleClick} />); // 👈 pass the mock
const button = screen.getByRole('button');
//Action
fireEvent.click(button);
//Assert
expect(handleClick).toHaveBeenCalled();
});
});
I have a component,
function TestComponent() {
const [visible, setVisible] = useState(true);
return (
<React.Fragment>
{visible && <Container>
I'm Visible
<button onClick={() => setVisible(false)}>
click to close
</button>
</Container>}
</React.Fragment>
);
}
I'm trying to test that on clicking the button the component should be invisible.
And I have following test case to test that,
test('Random Test', () => {
const randomComponent = shallow(<TestComponent />);
expect(randomComponent.find('Container')).toBeTruthy();
randomComponent.find('button').simulate('click');
expect(randomComponent.find('Container')).toBeFalsy();
});
Doesnt seem to work,
Getting error,
expect(received).toBeFalsy()
Received: {}
Any help would be appreciated?
I have a sneaky suspicion that this isnt the way to check if component is hidden. Would also appreciate if anyone could tell a better way.
Update #1:
expect(randomComponent.render().text()).toContain('I\'m Visible');
randomComponent.find('button').simulate('click');
expect(randomComponent.render().text()).toContain('');
Using the above testcases seem to work. Still looking for a better way.
Since .find() will always return an instance of ShallowWrapper class even if there is no node matched. See source code of .find() and .wrap(). It will NOT return a falsy value (null, undefined), so .toBeFalsy() assertion will always fail.
Use .exists([selector]) => Boolean
Returns whether or not any nodes exist in the wrapper. Or, if a selector is passed in, whether that selector has any matches in the wrapper.
import { shallow } from 'enzyme';
import React from 'react';
import { TestComponent } from './';
describe('68334346', () => {
it('should pass', () => {
const randomComponent = shallow(<TestComponent />);
expect(randomComponent.find('Container').exists()).toBeTruthy();
randomComponent.find('button').simulate('click');
expect(randomComponent.find('Container').exists()).toBeFalsy();
});
});
Use .toHaveLength(number) matcher
import { shallow } from 'enzyme';
import React from 'react';
import { TestComponent } from './';
describe('68334346', () => {
it('should pass', () => {
const randomComponent = shallow(<TestComponent />);
expect(randomComponent.find('Container')).toHaveLength(1);
randomComponent.find('button').simulate('click');
expect(randomComponent.find('Container')).toHaveLength(0);
});
});
I'm testing different things in a single component in separate tests. I want to not have to write render inside every single test, but the code underneath does not work.
I have understood that the cleanup function clears the rendered component after each test, so that is good.
import React from "react";
import { Router } from "react-router-dom";
import { render } from "#testing-library/react";
import "#testing-library/jest-dom";
import myComp from './myComp'
const renderComponent = () => {
return render(<myComp />);
};
describe("desc", () => {
beforeEach(() => {
const {getAllByText, getByText, getByRole} = renderComponent()
});
test("1", () => {
console.log(getAllByText) // not defined
});
test("2", () => {
console.log(getAllByText) // not defined
});
})
The setup above results in the error:
ReferenceError: getAllByText is not defined
My current workaround is to include renderComponent() function call in each test, but this does not look so clean.
test("1", () => {
const {getAllByText, getByText, getByRole} = renderComponent()
});
Attempt:
let result;
beforeEach(() => {
result = renderComponent();
}
test("renders success state", () => {
const { getByText } = result;
expect(getByText(noAccess)).toBeInTheDocument();
expect(getByText(applyForAccessButton)).toBeInTheDocument();});
Error I get then is:
TypeError: Cannot read property 'getByText' of undefined
getAllByText is local to beforeEach function, it's not defined in test scopes where it's accessed. In order to be workable this way, it should be:
let getAllByText, getByText, getByRole;
beforeEach(() => {
({getAllByText, getByText, getByRole} = renderComponent());
});
...
sorry late to the party but i will point out a good practice by kent c dodd
import { render, screen } from '#testing-library/react';
describe('Your Page',() => {
beforeEach(() => {
render(
<YourComponent />
);
})
test("renders success state", () => {
expect(screen.getByText(noAccess)).toBeInTheDocument();
})
})
here is the article refers to using screen.getByText rather than destructing it.
The benefit of using screen is you no longer need to keep the render call destructure up-to-date as you add/remove the queries you need. You only need to type screen. and let your editor's magic autocomplete take care of the rest.
link to the article : https://kentcdodds.com/blog/common-mistakes-with-react-testing-library
This is my test case
import React from 'react';
import { mount } from 'enzyme';
import CustomForm from '../index';
describe('Custom Form mount testing', () => {
let props;
let mountedCustomForm;
beforeEach(() => {
props = {
nav_module_id: 'null',
};
mountedCustomForm = undefined;
});
const customform = () => {
if (!mountedCustomForm) {
mountedCustomForm = mount(
<CustomForm {...props} />
);
}
return mountedCustomForm;
};
it('always renders a div', () => {
const divs = customform().find('div');
console.log(divs);
});
});
Whenever I run the test case using yarn test. It gives the following error TypeError: Cannot read property 'nav_module_id' of undefined.
I have already placed console at multiple places in order to see the value of props. It is getting set. But it couldn't just pass through the components and give the error mentioned above.
Any help would be appreciated been stuck for almost 2-3 days now.
You have to wrap the component that you want to test in beforeEach method such that it becomes available for all the 'it' blocks, and also you have to take the mocked props that you think you are getting into the original component.
import React from 'react'
import {expect} from 'chai'
import {shallow} from 'enzyme'
import sinon from 'sinon'
import {Map} from 'immutable'
import {ClusterToggle} from '../../../src/MapView/components/ClusterToggle'
describe('component tests for ClusterToggle', () => {
let dummydata
let wrapper
let props
beforeEach(() => {
dummydata = {
showClusters: true,
toggleClustering: () => {}
}
wrapper = shallow(<ClusterToggle {...dummydata} />)
props = wrapper.props()
})
describe(`ClusterToggle component tests`, () => {
it(`1. makes sure that component exists`, () => {
expect(wrapper).to.exist
})
it('2. makes sure that cluster toggle comp has input and label', () => {
expect(wrapper.find('input').length).to.eql(1)
expect(wrapper.find('label').length).to.eql(1)
})
it('3. simulating onChange for the input element', () => {
const spy = sinon.spy()
const hct = sinon.spy()
hct(wrapper.props(), 'toggleClustering')
spy(wrapper.instance(), 'handleClusterToggle')
wrapper.find('input').simulate('change')
expect(spy.calledOnce).to.eql(true)
expect(hct.calledOnce).to.eql(true)
})
})
})
I want to test that function passed from mapDispatchToProps was invoked when button clicking is simulated.
How to test that function which passed from mapDispatchToProps is invoked?
I tried to pass a mocked function by props, but it doesn't work. Any help will be appreciated.
Here below my fake class code and test example.
My component
// All required imports
class App extends React.Component<Props> {
render() {
const { onClick } = this.props;
return (
<>
<h1>Form</h1>
<input />
<button onClick={() => onClick()} />
</>
);
}
}
const mapStateToProps = (state) => {
return {
state
};
};
const mapDispatchToProps = (dispatch) => {
return {
onClick: () => dispatch(actions.onClick())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
My test file
import { configure, mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16/build/index';
import jsdom from 'jsdom';
import React from 'react';
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import ConnectedApp, { App } from './App';
function setUpDomEnvironment() {
const { JSDOM } = jsdom;
const dom = new JSDOM('<!doctype html><html><body></body></html>', { url: 'http://localhost/' });
const { window } = dom;
global.window = window;
global.document = window.document;
global.navigator = {
userAgent: 'node.js',
};
copyProps(window, global);
}
function copyProps(src, target) {
const props = Object.getOwnPropertyNames(src)
.filter(prop => typeof target[prop] === 'undefined')
.map(prop => Object.getOwnPropertyDescriptor(src, prop));
Object.defineProperties(target, props);
}
setUpDomEnvironment();
configure({ adapter: new Adapter() });
const mockStore = configureMockStore();
describe('App', () => {
describe('When App connected to store', () => {
describe('When button clicked', () => {
it('should not crush after click on login button', () => {
const onClick = jest.fn()
const store = mockStore(initialStates[1]);
const wrapper = mount(
<Provider store={store}>
<ConnectedApp />
</Provider>);
wrapper.find('button').simulate('click');
??? how to test that function passed from mapDispatchToProps was fired?
});
});
});
});
I recommend following the approach described in the docs and export the connected component as the default export for use in the application, and export the component itself as a named export for testing.
For the code above export the App class and test the click like this:
import * as React from 'react';
import { shallow } from 'enzyme';
import { App } from './code';
describe('App', () => {
it('should call props.onClick() when button is clicked', () => {
const onClick = jest.fn();
const wrapper = shallow(<App onClick={onClick} />);
wrapper.find('button').simulate('click');
expect(onClick).toHaveBeenCalledTimes(1);
});
});
shallow provides everything that is needed for testing the component itself. (shallow even calls React lifecycle methods as of Enzyme v3)
As you have found, to do a full rendering of the component requires a mock redux store and wrapping the component in a Provider. Besides adding a lot of complexity, this approach also ends up testing the mock store and all child components during the component unit tests.
I have found it much more effective to directly test the component, and to export and directly test mapStateToProps() and mapDispatchToProps() which is very easy since they should be pure functions.
The mapDispatchToProps() in the code above can be tested like this:
describe('mapDispatchToProps', () => {
it('should dispatch actions.onClick() when onClick() is called', () => {
const dispatch = jest.fn();
const props = mapDispatchToProps(dispatch);
props.onClick();
expect(dispatch).toHaveBeenCalledWith(actions.onClick());
});
});
This approach makes unit testing the component very simple since you can pass the component props directly, and makes it very simple to test that the component will be handed the correct props by the pure functions (or objects) passed to connect().
This ensures that the unit tests are simple and targeted. Testing that connect() and redux are working properly with the component and all of its child components in a full DOM rendering can be done in the e2e tests.