在测试时使用sinon会对React组件回调进行处理



我遇到了一些我无法解释的事情。我做了以下测试:

import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { expect } from 'chai';
import * as sinon from 'sinon';
import TestApp from '../../views/App.test';
import ReplicationForm from './ReplicationForm';
describe('ReplicationForm component', () => {
it('calls saveUrl on submit with new url', () => {
const saveUrlFake = sinon.fake();
const otherFake = (url: string) => console.log(url);
render(<ReplicationForm saveUrl={otherFake} />, { wrapper: TestApp });
(screen.getByLabelText('Hostname') as HTMLInputElement).value = 'https://example.com';
(screen.getByLabelText('Database') as HTMLInputElement).value = 'poi';
(screen.getByLabelText('Username') as HTMLInputElement).value = 'shaw';
(screen.getByLabelText('Password') as HTMLInputElement).value = 'IMissYouR00t';
userEvent.click(screen.getByRole('button', { name: 'Submit' }));
console.log(saveUrlFake.callCount);
expect(saveUrlFake.lastCall.firstArg).to.equal('https://shaw:IMissYouR00t@example.com/poi');
});
});

当我使用otherFake时,回调被正确调用,所需的输出被打印到控制台。不幸的是,如果我使用saveUrlFake,它永远不会被调用。saveUrlFake.callCount0,没有必要检查。

奇怪的是,当我自己用saveUrlFake('https://shaw:IMissYouR00t@example.com/poi')调用saveUrlFake时,测试工作了。我的问题是,为什么我的sinon fake没有被React组件调用?

tl;博士

所讨论的完整组件:

import styled from '@emotion/styled';
import { Button, TextField, withTheme } from '@material-ui/core';
import React from 'react';
import { useForm } from 'react-hook-form';
import { FormattedMessage, useIntl } from 'react-intl';
const StyledForm = withTheme(
styled.form`
display: flex;
flex-direction: column;
& > *:not(div:first-of-type) {
margin-top: ${({ theme }) => theme.spacing(2)}px;
}
`
);
type FormModel = {
origin: string;
database: string;
username: string;
password: string;
};
type ReplicationFormProps = {
url?: string;
saveUrl: (url: string) => void;
};
export default function ReplicationForm({ url, saveUrl }: ReplicationFormProps) {
const intl = useIntl();
const getDefaultValuesFromURL = (inputUrl: string): FormModel => {
const { origin, username, password, pathname } = new URL(inputUrl);
return {
origin,
username,
password,
database: pathname.replaceAll('/', '')
};
};
const defaultValues: FormModel = url
? getDefaultValuesFromURL(url)
: {
origin: '',
database: '',
username: '',
password: ''
};
const { handleSubmit, formState, register } = useForm<FormModel>({ defaultValues });
const onSubmit = (data: FormModel) => {
const newUrl = new URL(data.origin);
newUrl.username = data.username;
newUrl.password = data.password;
newUrl.pathname = `/${data.database}`;
saveUrl(newUrl.toString());
};
return (
<StyledForm onSubmit={handleSubmit(onSubmit)}>
<TextField
id="replication-form-origin"
name="origin"
inputRef={register}
label={intl.formatMessage({ id: 'label.hostname', defaultMessage: 'Hostname' })}
fullWidth
/>
<TextField
id="replication-form-database"
name="database"
inputRef={register}
label={intl.formatMessage({ id: 'label.database', defaultMessage: 'Database' })}
fullWidth
/>
<TextField
id="replication-form-username"
name="username"
inputRef={register}
label={intl.formatMessage({ id: 'label.username', defaultMessage: 'Username' })}
fullWidth
/>
<TextField
id="replication-form-password"
name="password"
type="password"
inputRef={register}
label={intl.formatMessage({ id: 'label.password', defaultMessage: 'Password' })}
fullWidth
/>
<Button type="submit" color="primary" disabled={formState.isDirty}>
<FormattedMessage id="action.submit" defaultMessage="Submit" />
</Button>
</StyledForm>
);
}

似乎回调是异步调用的,所以需要等待。测试库提供了waitFor(),可以在这种情况下提供帮助。

import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { expect } from 'chai';
import * as sinon from 'sinon';
import TestApp from '../../views/App.test';
import ReplicationForm from './ReplicationForm';
describe('ReplicationForm component', () => {
it('calls saveUrl on submit with new url', async () => {
const saveUrlFake = sinon.fake();
render(<ReplicationForm saveUrl={saveUrlFake} />, { wrapper: TestApp });
userEvent.type(screen.getByLabelText('Hostname *') as HTMLInputElement, 'https://example.com');
userEvent.type(screen.getByLabelText('Database *'), 'poi');
userEvent.type(screen.getByLabelText('Username *'), 'shaw');
userEvent.type(screen.getByLabelText('Password *'), 'IMissYouR00t');
userEvent.click(screen.getByRole('button', { name: 'Submit' }));
await waitFor(() => expect(saveUrlFake.called).to.be.true);
expect(saveUrlFake.lastCall.firstArg).to.equal('https://shaw:IMissYouR00t@example.com/poi');
});
});

最新更新