React container component's method test with jest - reactjs

I want to write unit test for React component's method.
The component's code is
export class LoginForm extends Component {
constructor() {
super();
this.tryLoginProp = this.tryLoginProp.bind(this)
}
render() {
return (
<div className="login-form">
<div className="form-input">
<CustomButton label="Log in"
class="login-button"
action={this.tryLoginProp}
id="login-button-loginform"
renderSpinner={this.props.renderSpinner}
/>
</div>
</div>
)
}
tryLoginProp () {
if (!this.props.renderSpinner) {
this.props.tryLoginProp()
}
}
}
const mapStateToProps = (state) => {
return {
login: state.login,
renderSpinner: state.layout.renderSpinner
};
};
const mapDispatchToProps = (dispatch) => ({
tryLoginProp: () => {
dispatch(tryLogin())
}
});
export default connect(mapStateToProps, mapDispatchToProps)(LoginForm);
I want to write unit test for tryLoginProp method, but I am not getting how to mock this.props.tryLoginProp function and pass the test case.
Current unit test is as follows:
describe('<LoginForm />', () => {
const initialState = {renderSpinner: false};
let wrapper;
beforeEach(() => {
wrapper = mount(<LoginForm {...initialState}/>);
});
it('renders without expolding', () => {
expect(wrapper.length).toEqual(1);
});
it('tryLoginProp should dispatch an action', () => {
expect(wrapper.tryLoginProp()). //test tryLoginProp method.
})
});
Please help me to write proper test case for this method.
Thanks

You can use wrapper.instance().tryLoginProp() to call the method...like this I think without testing it
it('tryLoginProp should dispatch an action', () => {
const mockedTryLoginProp = jest.fn();
const wrapper = shallow(
<LoginForm
tryLoginProp={mockedTryLoginProp}
renderSpinner={false}
/>
);
wrapper.instance().tryLoginProp();
expect(mockedTryLoginProp).toHaveBeenCalled();
})
On a side note, you may consider naming the internal function differently than the one being passed in to avoid confusion

Related

How to test a component that is conditionally rendered based on a hook value?

I am working on a React Native application and am very new to testing. I am trying to mock a hook that returns a true or false boolean based on the current user state. I need to mock the return value of the authState variable, and based on that, I should check if the component is rendered or not. But the jest mock is returning the same value only
useAuth.ts
export const useAuthState = () => {
const [authState, setAuthState] = useState<AuthState>();
useEffect(() => {
return authentication.subscribe(setAuthState);
}, []);
return authState;
};
MyComponent.tsx
export const MyComponent = () => {
const authState = useAuthState();
if (!authState) {
return null;
}
return <AnotherComponent />
}
MyComponent.test.tsx
import { MyComponent } from "./MyComponent"
jest.mock('../use-auth-state', () => {
return {
useAuthState: () => false,
};
});
const TestComponent = () => <MyComponent />
describe('MyComponent', () => {
it('Should return null if the authState is null', () => {
let testRenderer: ReactTestRenderer;
act(() => {
testRenderer = create(<TestComponent />);
});
const testInstance = testRenderer.getInstance();
expect(testInstance).toBeNull()
})
})
This is working fine. But, I am not able to mock useAuthState to be true as this false test case is failing. Am I doing it right? I feel like I am messing up something.
You want to change how useAuthState is mocked between tests, right? You can set your mock up as a spy instead and change the mock implementation between tests.
It's also a little more ergonomic to use the render method from react-testing-library. The easiest way would be to give your component a test ID and query for it. Something like the below
import { MyComponent } from "./MyComponent"
import * as useAuthState from '../use-auth-state';
const authStateSpy = jest.spyOn(useAuthState, 'default');
describe('MyComponent', () => {
it('Should return null if the authState is null', () => {
// you can use .mockImplementation at any time to change the mock behavior
authStateSpy.mockImplementation(() => false);
const { queryByTestId } = render(<MyComponent />;
expect(queryByTestId('testID')).toBeNull();
})

Unit Testing with Mocha,Enzyme dispatched functions in functional components which uses React Hooks + Redux

I am trying to test a dispatch from 'mapDispatchToProps' defined with a functional component which uses useEffect() hook and the function is called there.
export const MyComponent = (props) => {
useEffect(() => {
// Anything in here is fired on component mount.
props.registerInStore(props.id, false);
return () => {
// Anything in here is fired on component unmount.
props.resetInStore(props.id);
};
}, []);
const handleOnClick = () => {
props.toggle(props.id);
};
return (
<div >
{!props.isOpen ? (
<button
onClick={handleOnClick}>
Open
</button>
) : (
<button
onClick={handleOnClick}>
close
</button>
)}
</div>
);
};
const mapDispatchToProps = (dispatch) => ({
registerInStore(id, isOpen) {
dispatch(registerInStore(id, isOpen));
},
resetInStore(id) {
dispatch(resetInStore(id));
}
});
export default connect(null, mapDispatchToProps)(MyComponent);
In my unit tests with Mocha and enzyme i also want to test the dispatches inside 'mapDispatchToProps', what i did below does not seem to work :
describe('<MyComponent/>', () => {
let store = mockStore({
toggles: [
{
id: 10,
isOpen: true
}
]
}
});
const options = {
context: {store},
childContextTypes: {store: PropTypes.object.isRequired},
lifecycleExperimental: true
};
const setup = (inputProps = {}) => {
const props = {
id: 10,
isOpen: false,
registerInStore: expect.createSpy(),
resetInStore: expect.createSpy(),
toggle: expect.createSpy(),
...inputProps
};
const wrapper = mount(<MyComponent {...props} />, options);
return {
props,
wrapper
};
};
afterEach(() => {
expect.restoreSpies();
});
it('should dispatch', async () => {
const {wrapper}=setup();
await store.dispatch(wrapper.prop('registerInStore')(10,false));
/* i tried the commented way too instead of directly dispatching*/
// wrapper.prop('registerInStore')(10,false);
//await new Promise((resolve) => setTimeout(resolve, 50));
const expectedActions = [{type: 'REGISTER_IN_STORE', id: 10, isOpen: false}];
expect(store.getActions()).toEqual(expectedActions);
});
the store.getActions() is returning an empty array, i am new to React Hooks and testing, what am i doing wrong, any other solutions?.
Thanks in Advance.
worked by removing the spies e.g:-
const setup = (inputProps = {}) => {
const props = {
id: 10,
isOpen: false,
registerInStore:()=>null,
resetInStore: ()=>null,
toggle: ()=>null,
...inputProps
};
const wrapper = mount(<MyComponent {...props} />, options);
return {
props,
wrapper
};
};

Unable to spy on third party library function in test

I have a component that uses a third party library. When the button is clicked, it triggers the library's api.. gets the data from its callback and calls the this.props.SaveData with the returned data.
const Comp = () => {
let editorApi: any;
const triggerSave = () => {
// call to third party component api
editorApi.export((data) => {
this.props.SaveData(data.values);
})
}
return (
<>
<ThirdPartyEditor ref={(editor: any) => editorApi = editor} />
<button onClick={triggerSave}>Save Data</button>
</>
)
}
Since I'm testing the Comp component, I mocked third-party-editor in my test. But now when I try to test that this.props.SaveData was called I'm getting this error:
Cannot spy the export property because it is not a function; undefined given instead
Test:
jest.mock("third-party-editor", () => ({
default: () => "MyEditor"
}))
test("it calls SaveData when button is clicked", () => {
const editor = jest.spyOn(ThirdPartyEditor, "export").mockImplementation((data) => );
const { container, queryByText } = render(<Comp {...props} />);
act(() => fireEvent.submit(queryByText("Save Data")));
// fails to find export
})
EDIT: Different mocking approach
jest.mock("third-party-editor", () => ({
default: () => {
return class Mocked {
public editorApi = jest.fn();
public export = jest.fn();
public render() {
return "Editor";
}
};
},
}));
Returns the error TypeError: Cannot read property 'export' of undefined

window.history.back() not being called - jest enzyme

I wrote a simple unit test for the following. I am new to React JS testing - Trying to run a test using jest and enzyme.
render() {
return (
<div>
<div className="not-found">
<div className='_2'>WAS NOT FOUND</div>
<div onClick={() => {window.history.back()}} className='not-found-
btn' href='/'>GO BACK</div>
)
}
}
The file looks simple, there are no props and the only thing not being covered when the test is running is onClick . How could I test onClick and make sure the test is 100 % covered. Thanks
<div onClick={() => {window.history.back()}} className='not-found-
btn' href='/'>GO BACK</div>
file.test.js
// jest mock functions (mocks this.props.func)
const onClick = jest.fn();
// defining this.props
const baseProps = {
onClick,
}
describe(' Test', () => {
let wrapper;
let tree;
beforeEach(() => wrapper = shallow(<Component{...baseProps } />));
// before each test, shallow mount the Component
it('should render correctly', () => {
tree = renderer.create(<NotFound {...baseProps} />)
let treeJson = tree.toJSON()
expect(treeJson).toMatchSnapshot();
tree.unmount()
});
it('calls onClick event ', () => {
const mockOnClick = jest.fn();
const wrapper = shallow(
<NotFound onClick={mockOnClick} className='not-found-btn' />
);
const component = wrapper.shallow();
component.find('GO BACK').simulate('click');
expect(mockOnClick.mock.calls.length).toEqual(1);
I'd avoid using window history and instead use react-router-dom for MPAs. In addition, instead of using an anonymous function, you can use a PureComponent class (it's similar to a Component class, but it doesn't update state) with a method class function.
Working example: https://codesandbox.io/s/j3qo6ppxqy (this example uses react-router-dom and has a mix of integration and unit testing -- see the tests tab at the bottom of the page to run the tests and look for __test__ folders to see the code)
components/NotFound/notfound.js
import React, { PureComponent } from "react";
import { Button } from "antd";
export default class NotFound extends PureComponent {
handlePageBack = () => this.props.history.push("/");
render = () => (
<div className="notfound">
<h1>404 - Not Found!</h1>
<Button type="default" onClick={this.handlePageBack}>
Go Back
</Button>
</div>
);
}
components/NotFound/__tests__/notfound.test.js (as mentioned here, you can also test the class method, if desired)
import React from "react";
import { shallowComponent } from "../../../tests/utils";
import NotFound from "../notfound";
const mockGoBack = jest.fn();
const initialProps = {
history: {
goBack: mockGoBack
}
};
/*
the shallowComponent function below is a custom function in "tests/utils/index.js" that
simplifies shallow mounting a component with props and state
*/
const wrapper = shallowComponent(<NotFound {...initialProps} />);
describe("Not Found", () => {
it("renders without errors", () => {
const notfoundComponent = wrapper.find("div.notfound");
expect(notfoundComponent).toHaveLength(1);
});
it("pushes back a page when Go Back button is clicked", () => {
wrapper.find("Button").simulate("click");
expect(mockGoBack).toHaveBeenCalled();
});
});
window.history.back is being called, but it has a delay time. I can make it work using a Promise:
const Component = ()=> (<div>
<button onClick={()=> window.history.back()} className="btn btn-back">
Back
</button>
</div>)
Component.test.js
import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
const delayAction = (fn, time = 1000) =>
new Promise((resolve) => {
fn();
setTimeout(() => {
resolve();
}, time);
});
let container = null;
describe("App tests", () => {
afterEach(() => {
//unmount Component...
});
beforeEach(() => {
//mount Component
});
it("should call history.back()", async (done) => {
const btnBack = container.querySelector(".btn-back");
await act(() =>
delayAction(() => btnBack.dispatchEvent(new MouseEvent("click", { bubbles: true })))
);
// asserts..
done();
});
});

mocking a prop passed to a child component

I am new to jest. My component look like this.
class ComponentToTest extends React.Component {
searchName = () => {
return axios.get(...)
}
render() {
return(
<Form onSubmit={handleSubmit(save)}>
<SomeChildComponent searchName={ (searchTerm: string) => this.searchName(searchTerm)} />
</Form>
)
}
}
`ComponentToTest.test.js`
it('should call `handleSubmit` with `save`', () => {
const store = createStore((state) => state, { app: {} });
const onSave = jest.fn().mockReturnValue(() => {});
const props: any = { handleSubmit: onSave };
const tree = mount(
<Provider store={store}>
<ComponentToTest {...props} />
</Provider>,
);
tree.find('button').simulate('submit');
expect(onSave).toHaveBeenCalled();
});
I am getting error after running this test. is there any way to mock the call to searchName and return promise?

Resources