我正在尝试使用打字稿中的react-testing-library创建具有样式化Material-UI组件的测试。我发现很难访问组件的内部功能以进行模拟和断言。
Form.tsx
<?php
define('YES', 1);
define('NO', 0);
$USE_SSL = true;
$api_key = "MY_FILECRYPT_API_KEY";
$name = "My Testfolder";
$group = MyGroupID;
$container_id = MyContainerId;
// Here you grab the json from the file and convert it to an array
$json_string = file_get_contents("thejson.json");
$arr = json_decode($json_string, true);
$mirror_1 = $arr
// --------------------------
$postdata = http_build_query(array(
"fn" => "containerV2",
"sub" => "editV2",
"container_id"=> $container_id,
"api_key" => $api_key,
"name" => $name,
"mirror_1" => $mirror_1,
"captcha" => YES,
"allow_cnl" => YES,
"allow_dlc" => YES,
"allow_links" => NO,
"group" => $group
));
$opts = array('http' => array(
"method" => "POST",
"header" => "Content-type: application/x-www-form-urlencoded",
"content" => $postdata
));
$context = stream_context_edit($opts);
$result = file_get_contents('http'.(($USE_SSL)?'s':'').'://www.filecrypt.cc/api.php', false, $context);
if(!$result) {
throw new Exception("filecrypt.cc api down");
} else {
$json = json_decode($result);
echo "editd container: ".$json->container->link;
}
?>
Test.tsx
export const styles = ({ palette, spacing }: Theme) => createStyles({
root: {
flexGrow: 1,
},
paper: {
padding: spacing.unit * 2,
margin: spacing.unit * 2,
textAlign: 'center',
color: palette.text.secondary,
},
button: {
margin: spacing.unit * 2,
}
});
interface Props extends WithStyles<typeof styles> { };
export class ExampleForm extends Component<Props, State> {
async handleSubmit(event: React.FormEvent<HTMLFormElement>) {
// Handle form Submit
...
if (errors) {
window.alert('Some Error occurred');
return;
}
}
// render the form
}
export default withStyles(styles)(ExampleForm);
import FormWithStyles from './Form';
it('alerts on submit click', async () => {
jest.spyOn(window,'alert').mockImplementation(()=>{});
const spy = jest.spyOn(ActivityCreateStyles,'handleSubmit');
const { getByText, getByTestId } = render(<FormWithStyles />)
fireEvent.click(getByText('Submit'));
expect(spy).toHaveBeenCalledTimes(1);
expect(window.alert).toHaveBeenCalledTimes(1);
})
引发以下错误jest.spyOn
,可能是因为ExampleForm封装在withStyles中。
我还尝试直接导入Argument of type '"handleSubmit"' is not assignable to parameter of type 'never'.ts(2345)
组件并手动分配样式,但无法这样做:
ExampleForm
收到以下错误:import {ExampleForm, styles} from './Form';
it('alerts on submit click', async () => {
...
const { getByText, getByTestId } = render(<ActivityCreateForm classes={styles({palette,spacing})} />)
...
}
我发现由于类型强和包装好的组件,很难用Type Type '{ palette: any; spacing: any; }' is missing the following properties from type 'Theme': shape, breakpoints, direction, mixins, and 4 more.ts(2345)
和Material-UI
的{{1}}组件编写基本测试。请指导。
答案 0 :(得分:3)
首先,当您使用react-testing-library的render
方法时,您不必担心使用withStyles
或任何包装器,因为最后它会渲染组件在真实的dom中,因此您可以正常编写测试。
然后,据我所见,您正在做与开始 tests 时一样的事情(这意味着您将变得很熟练;)。您正在尝试模拟内部方法,但这不是最好的方法,因为您需要做的就是测试实际方法。
让我们想象一下,我们有一个Register
用户组件。
src / Register.tsx
import ... more cool things
import * as api from './api';
const Register = () => {
const [name, setName] = useState('');
const handleNameChange = (event) => {
setName(event.target.value);
};
const handleSubmit = (event) => {
event.preventDefault();
if (name) {
api.registerUser({ name });
}
};
return (
<form onSubmit={handleSubmit}>
<TextField
id='name'
name='name'
label='Name'
fullWidth
value={name}
onChange={handleNameChange}
/>
<Button data-testid='button' fullWidth type='submit' variant='contained'>
Save
</Button>
</form>
);
}
该组件非常简单,它是带有输入和按钮的表单。我们正在使用react hooks
来更改输入值,并基于触发api.registerUser
事件时是否调用handleSubmit
的输入值。
要测试组件,我们要做的第一件事是 mock api.registerUser
方法。
src / __ tests __ / Register.tsx
import * as api from '../api'
jest.mock('../api')
api.registerUser = jest.fn()
这将使我们看到是否调用了该方法。
接下来要做的是...编写测试,在这种情况下,我们可以测试两件事以查看handleSubmit
是否正常工作。
api.registerUser
。it('should not call api registerUser method', () => {
const { getByTestId } = render(<Register />)
fireEvent.click(getByTestId('button'))
expect(api.registerUser).toHaveBeenCalledTimes(0)
})
api.registerUser
。it('should call api registerUser method', () => {
const { getByLabelText, getByTestId } = render(<Register />)
fireEvent.change(getByLabelText('Name'), { target: { value: 'Steve Jobs' }})
fireEvent.click(getByTestId('button'))
expect(api.registerUser).toHaveBeenCalledTimes(1)
})
在最后的测试中,我们也正在测试handleNameChange
,因为我们正在更改名称:),因此name
不会为空,而registerUser
将被调用。 / p>
答案 1 :(得分:2)
您为什么不将enzyme
与Full DOM Rendering一起使用?
您可以使用simulate
方法来模拟已安装组件上的事件。
class Foo extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
render() {
const { count } = this.state;
return (
<div>
<div className={`clicks-${count}`}>
{count} clicks
</div>
<a href="url" onClick={() => { this.setState({ count: count + 1 }); }}>
Increment
</a>
</div>
);
}
}
const wrapper = mount(<Foo />);
expect(wrapper.find('.clicks-0').length).to.equal(1);
wrapper.find('a').simulate('click');
expect(wrapper.find('.clicks-1').length).to.equal(1);
答案 2 :(得分:2)
您可以使用unwrap来包装已包装样式的组件,然后对其进行测试
import { unwrap } from '@material-ui/core/test-utils';
import {ExampleForm, styles} from './Form';
it('alerts on submit click', async () => {
...
const unwrapped = unwrap(ExampleForm);
...
}
然后您就可以对未包装的对象进行测试