یه برنامه نویس معمولی لینوکس کار
نوشتن تست در ReactJs برای هوک ها
داخل یکی از مقاله های قبلیم در مورد نوشتنن تست برای Reactjs صحبت کردیم .
مشکلی که داشتم چی بود؟ اینکه من میخواستم داخل یک functional component که هوک داره ، یک تست بنویسم . من داخل کامپوننت هم از Material-ui استفاده کردم چون کار باهاش برام لذت بخشه . ( سلیقه ای دیگه . منم بد سلیقم ).
هرکار میکردم نمیتونستم به سلکتورها برسم . به هر دری زدم و متوجه شدم که برای هوک ها باید کارهای دیگه ای کرد و به شکل دیگه نوشت . موضوع اینه که وقتی ما Functional Component داریم مینویسیم ، Enzyme نمیتونه کامپوننت رو رندر کنه . چون باید حتما کامپوننت یک instance از کلاس باشه . اما ما Function داریم مینویسیم .
بعد از کلی زیر و رو کردن فهمیدم باید از یک کتابخانه دیگه استفاده کنیم .
کتابخانه react-testing-library همونیه که میتونه کمک کنه به ما .
حالا با یک مثال ساده نگاه میندازیم به موضوع :
import React, {useState} from 'react'
import {withStyles} from '@material-ui/core/styles';
import PropTypes from 'prop-types';
import InputAdornment from '@material-ui/core/InputAdornment';
import TextField from '@material-ui/core/TextField';
import classNames from 'classnames';
import Grid from "@material-ui/core/Grid";
import {isNumber} from "persian-regex";
import Fab from '@material-ui/core/Fab';
const styles = theme => ({
root: {
display: 'flex',
flexWrap: 'wrap',
},
margin: {
margin: theme.spacing.unit,
height: 39
},
textField: {
flexBasis: 200,
},
rootGrid: {
padding: 0,
height: '100%',
overflow: 'hidden'
},
rootGridItem: {
textAlign: 'left'
},
adornment: {
'& p':
{
color: '#ffffff',
fontSize: 12,
display: 'inherit',
width: 85,
paddingRight: 5
}
},
inputLabel: {
color: '#ffffff'
},
extendedIcon: {
marginRight: theme.spacing.unit,
},
fabButton: {
width: '100%',
margin: '0 auto',
textAlign: 'center',
borderRadius: 5
},
fabButtons: {
padding: 6
},
buyFab: {
backgroundColor: '#12b886',
color: '#ffffff'
},
sellFab: {
backgroundColor: '#fa5252',
color: '#ffffff'
}
});
function Limit(props) {
const [buyAmount, setBuyAmount] = useState(0);
const [alert, setAlert] = useState("");
function handleBuy() {
setAlert("")
if (buyAmount === 0 || buyAmount === "")
setAlert("missing inputs")
setTimeout(() => {
setAlert("");
}, 2000)
}
function handleChange(event, name) {
const amount = event.target.value;
if (amount !== undefined)
if (isNumber(amount))
switch (name) {
case 'buyAmount':
setBuyAmount(amount);
break;
}
}
const {classes} = props;
return (
<React.Fragment>
<Grid container classes={{container: classes.rootGrid}}>
<Grid item xs={6} className={classes.rootGridItem}>
<TextField
id="filled-adornment-amount"
className={classNames(classes.margin, classes.textField)}
variant="filled"
InputLabelProps={{
classes: {root: classes.inputLabel}
}}
value={buyAmount > 0 ? buyAmount : ""}
={(e) => handleChange(e, 'buyAmount')}
InputProps={{
inputProps: {'data-testid': "buyAmount"},
classes: {root: classes.inputLabel},
startAdornment: <InputAdornment position="start" classes={{root: classes.adornment}}>
Amount (IRR)
</InputAdornment>,
}}
/>
</Grid>
<Grid item xs={6} className={classNames(classes.rootGridItem, classes.fabButtons)}>
<Fab
data-testid="buy"
classes={{
root: classes.buyFab
}}
={handleBuy}
variant="extended"
size="large"
color="primary"
aria-label="Buy"
className={classes.fabButton}
>
Buy
</Fab>
</Grid>
<div data-testid="alert">{alert}</div>
</Grid>
</React.Fragment>
)
}
Limit.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(Limit);
داخل این مثال از material-ui و یکی از پکیج هایی که خودم نوشتم به اسم persian-regex استفاده کردم .
حالا میخوام تستم رو درست کنم . میرم داخل پوشه __tests__ و یک فایل به اسم Limit.js میسازم . تست ما از این پوشه پیروی میکنه .
داخل ترمینال پکیجمون رو نصب میکنیم :
npm i react-testing-library --save-dev
حالا داخل Limit.js که ساختیم برای تست کد زیر رو میزنیم :
import React from 'react';
import Limit from "../Components/Limit"; // limit component we made above
import 'react-testing-library/cleanup-after-each'
import {render, fireEvent} from 'react-testing-library'
import {isNumber} from "persian-regex";
describe('Test Limit Component', () => {
describe('Click on Buy Buttons and check validation', () => {
describe('should click on Buy Button ', () => {
it('should return false , cause inputs are empty', () => {
const {getByTestId} = render(<Limit/>)
const buyButton = getByTestId('buy');
const buyAmountInput = getByTestId('buyAmount');
const alert = getByTestId('alert');
fireEvent.change(buyAmountInput, {target: {buyAmount: ""}});
fireEvent.click(buyButton)
if (buyAmountInput.buyAmount === "") {
expect(alert).toContain('missing inputs')
}
})
it('write into buy Amount input and it should get value : 123456789', () => {
const {getByTestId} = render(<Limit/>)
const number = '123456789';
const buyAmountInput = getByTestId('buyAmount');
fireEvent.change(buyAmountInput, {target: {buyAmount: isNumber(number) ? number : ''}})
expect(buyAmountInput.buyAmount).toEqual(number)
})
it('write into buy Amount input and it should get empty value because input is not valid', () => {
const {getByTestId} = render(<Limit/>)
const number = 'hi there';
const buyAmountInput = getByTestId('buyAmount');
fireEvent.change(buyAmountInput, {target: {buyAmount: isNumber(number) ? number : ''}})
expect(buyAmountInput.buyAmount).toEqual('')
})
})
})
})
داخل کد بالا سه شرط نوشتم . یکی کلیک کردن روی دکمه خرید ، بدون وارد کردن مقدار ، یکی کلیک با مقدار درست یکی هم با مقدار اشتباه و validate کدن input . حالا بعد از اجرای دستور npm run test ، تست یکی یکی اجرا میشه و درست بودنشون چک میشه .
مطلبی دیگر از این انتشارات
رعایت Best Practice ها در React
مطلبی دیگر از این انتشارات
از تشخیص کلیک خارج از کامپوننت تا Type Assertions در تایپاسکریپت
مطلبی دیگر از این انتشارات
مثال کاملی از پیاده سازی یک برنامه وبی پایه بر React