Jest enzyme test router with props
I have react application and started covering it with unit tests. My app file contains:
export default class App extends Component {
render() {
return (
<Provider store={store}>
<Router history={history}>
<div>
<Header />
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/detail/:collectionName/:RecordId/:firmId" component={DetailDialog} />
<Route exact path="/dashboard" component={Dashboard} />
<Route path="/auth-login" component={LogIn} />
<Route path="/auth-register" component={Register} />
<Route path="/user-manager" component={this.props.decoded.role==='Admin' ? Admin : Home} />
</Switch>
</div>
</Router>
</Provider>
);
}
}
I want to make next test: go to login, check href and by href check component.
it.only('test right component is attached', () => {
const wrapper = shallow(<Login loginUser={fn}/>);
const registerLink = wrapper.find("[data-qa='sign-up']").props().href;
const history = jest.fn();
const props = {
decoded: {role: 'Admin'}
}
const router = mount(
<MemoryRouter initialEntries={[registerLink]}>
<App history={history} {...props} />
</MemoryRouter>
);
expect(router.find(Register)).toHaveLength(1);
});
Until mounting everything is ok. But on mount I receive the next error:
console.error Error: Uncaught [Error: Unable to find node on an unmounted component.]
What am I doing wrong? How can I mount the component and check that Register is inside?
See also questions close to this topic
-
Testing vue 3 async setup
I can't quite figure out how to test my async setup vue 3 component.
<script> import { onMounted, toRefs } from 'vue' export default { name: 'HelloWorld', props: { msg: String }, async setup(props) { const { msg } = toRefs(props) // some awaited thing return { msg } } } </script>
Wrapping this component with a
Suspense
component works as expected. However when running tests:import { mount } from '@vue/test-utils' import HelloWorld from './HelloWorld.vue' describe('HelloWorld.vue', () => { it('renders props.msg when passed', () => { const msg = 'new message' const wrapper = mount(HelloWorld, { props: { msg } }) expect(wrapper.text()).toMatch(msg) }) }
This will throw an error
TypeError: Cannot read property 'addEventListener' of null
on theshallowMount
ormount
. I've also tried making them async but that also doesn't work throwing the same error.The only solution is using
onMounted
hook. But that's not how Suspense is supposed to be used. Any ideas? Or is this simply not yet supported? -
How to use jest to mock a function just for some specified arguments, but uses its original logic for any other arguments?
Say I have a function:
function complexCompute(num: number): string { switch(num) { case 1: return '...something...'; case 2: return '...something...'; case 3: return '...something...'; // more cases }
It is used many times in the code I want to test, but I want to mock it like this:
- when I pass the argument
num
is1
,complexCompute
returns my mocked stringmy-mock-string1
- when I pass any other argument, it uses its original logic
I can't find a way to do it, since if I mocked the module:
jest.mock('./complexCompute')
The mocked
complexCompute
doesn't have original logic. I have to define the mock value for argument1
, and also rewrite the whole logic for other arguments.Is there any way to do it?
- when I pass the argument
-
Jest ReferenceError: You are trying to `import` a file after the Jest environment has been torn down
I am trying to test my APIs using jest.
I am having this error show after each time I run the tests
PASS test/auth.test.js √ Create a new user (237 ms) √ SignIn with user (133 ms) √ Delete The user (144 ms) Test Suites: 1 passed, 1 total Tests: 3 passed, 3 total Snapshots: 0 total Time: 2.173 s, estimated 8 s Ran all test suites. ReferenceError: You are trying to `import` a file after the Jest environment has been torn down.
my test file
const auth = require("../routes/auth"); const fetch = require('node-fetch'); fetch.Promise = require("bluebird"); (async () => { beforeAll(done =>{ jest.useFakeTimers(); done(); }) afterAll(done => { done(); }) jest.setTimeout(10000) test("Create a new user", async () => { const status = await fetch(URL + "/api/auth/signUp", { method:"POST", headers: { 'Content-Type': 'application/json;charset=utf-8' }, body:JSON.stringify(SignUpReq) }) .then(res => res.status) await expect(status).toBe(201); }) test("SignIn with user", async () => { let status = await fetch(URL + "/api/auth/signin", { method:"POST", headers: { 'Content-Type': 'application/json;charset=utf-8' }, body:JSON.stringify(deleteUserReq) }) .then(res => res.status) await expect(status).toBe(200); }) test("Delete The user", async () => { const status = await fetch(URL + "/api/auth/deleteUser", { method:"DELETE", headers: { 'Content-Type': 'application/json;charset=utf-8', 'Authorization': 'Bearer <token>' }, body:JSON.stringify(deleteUserReq) }) .then(res => res.status) await expect(status).toBe(200); }) })()
The APIs test the actual database, not a mock DB
I tried
mongoose.connection.close()
jest.useFakeTimers();
changing jest timeout
adding done before and after all
using await before each expect
still, the error shows up
my env:
"node": "V12.19.0" "bcrypt": "^5.0.0", "bluebird": "^3.7.2", "cors": "^2.8.5", "express": "^4.17.1", "jsonwebtoken": "^8.5.1", "md5": "^2.3.0", "mongoose": "^5.11.8", "node-fetch": "^2.6.1", "jest": "^26.6.3"
my DB is hosted on mongo atlas
How can I remove this error message?
-
testing form validation using Enzyme
I'm trying to test if the error shows up on the screen when user submit the form with empty inputs!
I did this test and everything looks good to me ,but it says that length is 0 which means it didn't show up, where is my mistake ?
import React, { useState } from 'react'; import { login } from '../../../services/account-service'; import validate from '../../../utility/login-validate'; import { Button, Error, Form, Input, Label, NavLink, Sign, SignBtn } from './signin-style'; /** * Component to log in the website if you have valid information, and display errors if the information is invalid. */ function Signin() { const [email, setemail] = useState(''); const [errors, setErrors] = useState(null); const [password, setPassword] = useState(''); /** *this methode called when the user presses the submit button, first it will check if there is errors, if not it will submit the form! * * @param {React.SyntheticEvent} event click on submit button event! */ const handleSubmit = async (event) => { event.preventDefault(); /** * Function that check if the user inserted wrong information and put it on `error` variable!, then it will update the 'errors' state! */ const error = validate(email, password); setErrors(error); await login(email, password); }; /** * Method that handle the change on { email input } by taking the input value and setting it on 'email' state! * * @param {React.SyntheticEvent} event when user type in email input! */ const handleemailChange = (event) => { const user = event.currentTarget.value; setemail(user); }; /** * Method that handle the change on { password input } by taking the input value and setting it on 'password' state! * * @param {React.SyntheticEvent} event When user type in password input */ const handlePasswordChange = (event) => { const pass = event.currentTarget.value; setPassword(pass); }; return ( <Sign> <h1>Sign In</h1> <NavLink to="/login">Need an account?</NavLink> {errors ? <Error id="error">* Invalid email or password!</Error> : null} <Form> <div> <Label htmlFor="email"> <Input id="email" onChange={handleemailChange} placeholder="Email" type="text" value={email} /> </Label> </div> <div> <Label htmlFor="password"> <Input id="password" onChange={handlePasswordChange} placeholder="Password" type="password" value={password} /> </Label> </div> <SignBtn> <Button type="submit" onClick={handleSubmit}> Sign in </Button> </SignBtn> </Form> </Sign> ); } export default Signin;
my test :
import { shallow } from 'enzyme'; import React from 'react'; import '../../../setup-tests'; import Signin from './Signin'; describe('Signin component', () => { let wrapper; beforeEach(() => { wrapper = shallow(<Signin />); }); it('should display error on the screen on click', () => { const emailInput = wrapper.find('#email'); const passwordInput = wrapper.find('#password'); passwordInput.simulate('change', { currentTarget: { value: '' } }); emailInput.simulate('change', { currentTarget: { value: '' } }); const submitButton = wrapper.find('[type="submit"]'); submitButton.simulate('click'); expect(wrapper.find('#error')).toHaveLength(1); }); });
test return this when I run it :
Expected length: 1, Received length: 0, Received object: {}
-
callback function for `scroll` event doesn't get covered by enzyme test. (React.js)
This is my part of React component
... componentDidMount(): void { window.addEventListener('scroll', this.handleScroll, true); } handleScroll() { const hello = 'assign'; this.setState({blah: 'blah'}); /// whole function is not covered. } ...
enzyme test
describe('when is scrolled', () => { beforeEach(() => { wrapper.simulate('scroll', true); <-- simulate scroll }); test('should match snapshot', () => { expect(wrapper).toMatchSnapshot(); <<< should cover `handleScroll` }) });
This should cover
handleScroll
but enzyme test doesn't cover the whole function (for code coverage) -
Jest tests keep failing for React component that renders various HTML elements based on type by using switch statement
I have a React Component that takes an array and iterates over each node, styling and rendering HTML elements based on the types found within the array.
I have everything running properly and now I'm trying to write a test using Jest to check that:
- The component doesn't render anything when it receives an empty array
- The component renders the appropriate HTML elements based on type when it receives a populated array
I'm relatively new to Jest and testing and I'm not sure how to write the tests to check that the appropriate elements have rendered. Also my null check test keeps failing with the following error message:
FAIL src/components/RenderTextComponent.spec.js ● <RenderTextComponent /> › renders null for empty sections array expect(received).toEqual(expected) Expected value to equal: null Received: <RenderTextComponent /> Difference: Comparing two different types of values. Expected null but received object. 27 | 28 | it('renders null for empty sections array', () => { > 29 | expect(<RenderTextComponent {...emptySections} />).toEqual(null) | ^ 30 | }) 31 | 32 | it('renders', () => { at Object.it (src/components/RenderTextComponent.spec.js:29:50)
This is my testing file:
import React from 'react'; import { shallow } from 'enzyme' import RenderTextComponent from './RenderTextComponent' describe('<RenderTextComponent />', () => { let wrapper; const sections = {} const populatedSections = [ { type: "subtitle", text: ["This is a really cool subtitle filled with words of wonder"] }, { type: "body", text: ["This is an even cooler sentence that shows up as a paragraph.", "And this is a second sentence that shows up as a second paragraph."] } ] const emptySections = [] beforeEach(() => { wrapper = shallow(<RenderTextComponent {...sections} />); }); it('renders null for empty sections array', () => { expect(<RenderTextComponent {...emptySections} />).toEqual(null) }) it('renders', () => { expect(<RenderTextComponent {...populatedSections} />).toEqual(expect.anything()) }) })
And this is my original component that I'm testing:
import React from "react"; import styled from "styled-components"; function renderElements(sections) { const elements = []; if (!sections) return null; sections.map((section) => { switch (section.type) { case "title": return elements.push( section.text.map((string) => <Title>{string}</Title>) ); case "subtitle": return elements.push( section.text.map((string) => <Subtitle>{string}</Subtitle>) ); case "body": return elements.push( section.text.map((string) => <Body>{string}</Body>) ); default: return null; } }); return elements; } const RenderTextComponent = ({ sections }) => { return <>{renderElements(sections)}</>; }; export default RenderTextComponent; const Title = styled.h1` font-size: 28px; `; const Subtitle = styled.h4` font-size: 24px; `; const Body = styled.p` font-size: 18px; `