如何使用react-testing-library测试包装在withStyles中的样式化Material-UI组件?

时间:2019-05-01 15:59:23

标签: reactjs typescript jestjs material-ui react-testing-library

我正在尝试使用打字稿中的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}}组件编写基本测试。请指导。

3 个答案:

答案 0 :(得分:3)

首先,当您使用react-testing-libraryrender方法时,您不必担心使用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是否正常工作。

  1. 如果姓名为空,请勿呼叫api.registerUser
it('should not call api registerUser method', () => {
  const { getByTestId } = render(<Register />)
  fireEvent.click(getByTestId('button'))
  expect(api.registerUser).toHaveBeenCalledTimes(0)
})
  1. 如果名称不为空,请致电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>

在此repo中,具有 withStyles typescript 的示例。
该演示为here

答案 1 :(得分:2)

您为什么不将enzymeFull 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);
    ...
}

然后您就可以对未包装的对象进行测试