【发布时间】:2021-05-20 09:34:19
【问题描述】:
这是一个简单的reactjs 项目的测试用例,在这种情况下,我尝试针对textContent 进行测试。
container 只是一个div 元素,它是我们的渲染目标。而Simple 是返回反应组件的函数。
textContent 是字符串,但分配的内存长度和“逻辑”内存的长度不同。所以,首先我尝试使用trim(),它不起作用并认为它很奇怪,然后我尝试使用split() 和join(),这不是最好的解决方案,但它应该可以工作。
it("simple test", function () {
act(function () {
const Simple = Template.bind({});
render(Simple(props), container);
});
let received = container.textContent.split(""); // ["A", "B", "C", "", ""]
received = received.join(""); // "ABC"
console.log(received.length); // but length is still 5
let expected = values[0].name; // "ABC", with length 3
expect(received).toBe(expected); // in conclusion, test case fails
});
编辑:可重现的示例
两个文件都在同一个目录中。
// CurrencyTextField.test.js
import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
import CurrencyTextField from "./CurrencyTextField";
let container = null;
let Template;
let currencies;
let props;
beforeEach(function () {
container = document.createElement("div");
document.body.appendChild(container);
props = {
currencies,
current: currencies[0],
handleValueChange: undefined,
handleCurrencyChange: undefined,
style: {
labelWidth: 50,
classes: { formControl: "" },
},
error: { isError: false, errorText: "" },
};
});
afterEach(function () {
unmountComponentAtNode(container);
container.remove();
container = null;
});
beforeAll(() => {
Template = (args) => <CurrencyTextField {...args} />;
currencies = [
{
name: "USD",
flag: "",
symbo: "$",
value: "1.54",
error: {
isError: false,
errorText: "",
},
},
];
});
it("simple test", function () {
act(function () {
const Simple = Template.bind({});
render(Simple(props), container);
});
let received = container.textContent.trim(); // here is the problem mentioned above.
console.log(received);
console.log(received.length);
let expected = currencies[0].name;
expect(received).toBe(expected);
});
// CurrencyTextField.js
import React from "react";
import TextField from "@material-ui/core/TextField";
import InputAdornment from "@material-ui/core/InputAdornment";
import FormControl from "@material-ui/core/FormControl";
import FormHelperText from "@material-ui/core/FormHelperText";
import InputLabel from "@material-ui/core/InputLabel";
import OutlinedInput from "@material-ui/core/OutlinedInput";
import MenuItem from "@material-ui/core/MenuItem";
import PropTypes from "prop-types";
const CurrencyTextField = (props) => {
const {
currencies,
current,
handleValueChange,
handleCurrencyChange,
style,
} = props;
const { classes, labelWidth } = style;
const { formControl } = classes;
const { name, value } = current;
const { errorText, isError } = current.error;
return (
<FormControl className={formControl} variant="outlined">
<InputLabel htmlFor={`${name}-currency-value`}>{name}</InputLabel>
<OutlinedInput
id={`${name}-currency-value`}
value={value}
onChange={handleValueChange}
error={isError}
endAdornment={
<InputAdornment position="end">
<TextField select value={name} onChange={handleCurrencyChange}>
{currencies.map((currency) => (
<MenuItem key={currency.name} value={currency.name}>
{currency.symbol}
</MenuItem>
))}
</TextField>
</InputAdornment>
}
labelWidth={labelWidth | 55}
/>
<FormHelperText id={`${name}-helper-text`}>{errorText}</FormHelperText>
</FormControl>
);
};
const _c = PropTypes.shape({
name: PropTypes.string,
flag: PropTypes.string,
symbol: PropTypes.string,
value: PropTypes.string,
error: PropTypes.shape({
isError: PropTypes.bool,
errorText: PropTypes.string,
}),
});
CurrencyTextField.propTypes = {
currencies: PropTypes.arrayOf(_c),
current: _c,
handleValueChange: PropTypes.func,
handleCurrencyChange: PropTypes.func,
style: PropTypes.object,
};
export default CurrencyTextField;
package.json:
{
"name": "exchange",
"version": "0.1.0",
"private": true,
"dependencies": {
"@material-ui/core": "4.11.3",
"@material-ui/icons": "4.11.2",
"@storybook/cli": "6.1.18",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"clsx": "1.1.1",
"money": "0.2.0",
"nice-color-palettes": "3.0.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-scripts": "4.0.2",
"web-vitals": "^1.0.1"
},
"scripts": {
"start": "BROWSER=none react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"storybook": "BROWSER=none start-storybook -p 6006 -s public",
"build-storybook": "build-storybook -s public"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@storybook/addon-actions": "^6.1.18",
"@storybook/addon-essentials": "^6.1.18",
"@storybook/addon-links": "^6.1.18",
"@storybook/node-logger": "^6.1.18",
"@storybook/preset-create-react-app": "^3.1.6",
"@storybook/react": "^6.1.18"
}
}
DOM 折叠是因为 OutlinedInput 组件而发生的。
而yarn test 失败了。
【问题讨论】:
-
它不会删除空字符串,因为它不是为此而设计的。您可以使用
.filter()删除不需要的元素,然后在该结果上使用.join()。 -
我想不出在
""上拆分会导致数组以两个空字符串结尾的任何上下文。尝试提供一个minimal reproducible example,其中包含您对该函数的输入。 -
// but length is still 5- 不能用您显示的代码真正重现。字符串ABC几乎不能有长度 5,这没有任何意义。这里唯一可能的解释是您的输入数据与您所说的不同。 -
收到的
textContent是以两个空格字符结尾的字符串,这是折叠DOM 的结果,就像@AKX 所说的那样,但trim()应该处理这种情况,不是吗?跨度> -
“收到的 textContent 是以两个空格字符结尾的字符串” - 然后将其拆分为
""不应该像你展示的那样得到一个数组,你会获取两个元素,每个元素包含一个空格字符,而不是两个 empty 字符串。
标签: javascript reactjs jestjs material-ui