我正在处理一个项目,并试图对输入字段的简单状态更改进行单元测试.在try 使用Reaction测试库userEvent.setup()
方法模拟onChange事件时,我总是得到一个失败的断言.
测试:
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { MockedProvider } from '@apollo/client/testing';
import CreateClientForm from '../../src/components/CreateClientForm';
import { vi } from 'vitest';
const mockProps: CreateClientFormProps = {
clientName: '',
setClientName: vi.fn(),
clientEmail: '',
setClientEmail: vi.fn(),
clientPhone: '',
setClientPhone: vi.fn(),
};
beforeEach(() => {
render(
<MockedProvider>
<CreateClientForm {...mockProps} />
</MockedProvider>
);
});
describe('Create Client Form In Modal', () => {
it('renders the correct initial input values', () => {
expect(screen.getByLabelText('Client Name:')).toBeInTheDocument();
expect(screen.getByLabelText('Client Email:')).toBeInTheDocument();
expect(screen.getByLabelText('Client Phone:')).toBeInTheDocument();
});
it('updates state values as a user types', async () => {
const user = userEvent.setup();
const nameInput = screen.getByRole('textbox', {
name: 'Client Name:',
}) as HTMLInputElement;
// Check initial value
expect(nameInput.value).toEqual('');
// Simulate user typing
await user.type(nameInput, 'Johnny Bravo');
console.log(
screen.getByRole('textbox', {
name: 'Client Name:',
})
);
// Verify updated value after change event (FAILING HERE)
//expect(nameInput.value).toEqual('Johnny Bravo');
});
});
相关组件:
import LabelWithInput from './common/LabelWithInput';
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
};
const CreateClientForm = ({
clientName,
setClientName,
clientEmail,
setClientEmail,
clientPhone,
setClientPhone,
}: CreateClientFormProps) => {
return (
<form onSubmit={handleSubmit}>
<LabelWithInput
label='Client Name:'
type='text'
id='clientName'
value={clientName}
onChange={(e) => setClientName(e.target.value)}
className='form-control mb-2'
data-testid='client-name'
/>
<LabelWithInput
label='Client Email:'
type='text'
id='clientEmail'
value={clientEmail}
onChange={(e) => setClientEmail(e.target.value)}
className='form-control mb-2'
data-testid='client-email'
/>
<LabelWithInput
label='Client Phone:'
type='text'
id='clientPhone'
value={clientPhone}
onChange={(e) => setClientPhone(e.target.value)}
className='form-control mb-2'
data-testid='client-phone'
/>
<button className='btn btn-primary'>Submit</button>
</form>
);
};
export default CreateClientForm;
LabelWithInput组件:
import { type ComponentPropsWithoutRef } from 'react';
type LabelWithInputProps = {
id: string;
label: string;
} & ComponentPropsWithoutRef<'input'>;
const LabelWithInput = ({ id, label, ...props }: LabelWithInputProps) => {
return (
<div>
<label htmlFor={id}>{label}</label>
<input id={id} {...props} />
</div>
);
};
export default LabelWithInput;
终端出错:
FAIL tests/components/CreateClientForm.test.tsx > Create Client Form In Modal > updates state values as a user types
AssertionError: expected '' to deeply equal 'Johnny Bravo'
- Expected
+ Received
- Johnny Bravo
❯ tests/components/CreateClientForm.test.tsx:51:29
49|
50| // Verify updated value after change event
51| expect(nameInput.value).toEqual('Johnny Bravo');
| ^
52| });
53| });
使用React 18.json相关包deps:
"@testing-library/jest-dom": "^6.1.4",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.5.1",
"@types/jest": "^29.5.8",
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"@vitejs/plugin-react": "^4.0.3",
"dom-testing-library": "^5.0.0",
"eslint": "^8.45.0",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
"jsdom": "^22.1.0",
"ts-node": "^10.9.1",
"vite": "^4.4.5",
"vitest": "^0.34.6"
这是输入的console.log在"写入"之后的输出:
<ref *1> HTMLInputElement {
'__reactFiber$ryal2e46zro': FiberNode {
tag: 5,
key: null,
elementType: 'input',
type: 'input',
stateNode: [Circular *1],
return: FiberNode {
tag: 5,
key: null,
elementType: 'div',
type: 'div',
stateNode: [HTMLDivElement],
return: [FiberNode],
child: [FiberNode],
sibling: null,
index: 0,
ref: null,
pendingProps: [Object],
memoizedProps: [Object],
updateQueue: null,
memoizedState: null,
dependencies: null,
mode: 1,
flags: 0,
subtreeFlags: 1048576,
deletions: null,
lanes: 0,
childLanes: 0,
alternate: null,
actualDuration: 0,
actualStartTime: -1,
selfBaseDuration: 0,
treeBaseDuration: 0,
_debugSource: [Object],
_debugOwner: [FiberNode],
_debugNeedsRemount: false,
_debugHookTypes: null
},
child: null,
sibling: null,
index: 1,
ref: null,
pendingProps: {
id: 'clientName',
type: 'text',
value: '',
onChange: [Function: onChange],
className: 'form-control mb-2',
'data-testid': 'client-name'
},
memoizedProps: {
id: 'clientName',
type: 'text',
value: '',
onChange: [Function: onChange],
className: 'form-control mb-2',
'data-testid': 'client-name'
},
updateQueue: null,
memoizedState: null,
dependencies: null,
mode: 1,
flags: 1048576,
subtreeFlags: 0,
deletions: null,
lanes: 0,
childLanes: 0,
alternate: null,
actualDuration: 0,
actualStartTime: -1,
selfBaseDuration: 0,
treeBaseDuration: 0,
_debugSource: {
fileName: '/home/sam/code/react_pro/mern_project_mgmt/client/src/components/common/LabelWithInput.tsx',
lineNumber: 12,
columnNumber: 7
},
_debugOwner: FiberNode {
tag: 0,
key: null,
elementType: [Function: LabelWithInput],
type: [Function: LabelWithInput],
stateNode: null,
return: [FiberNode],
child: [FiberNode],
sibling: [FiberNode],
index: 0,
ref: null,
pendingProps: [Object],
memoizedProps: [Object],
updateQueue: null,
memoizedState: null,
dependencies: null,
mode: 1,
flags: 1048577,
subtreeFlags: 1048576,
deletions: null,
lanes: 0,
childLanes: 0,
alternate: null,
actualDuration: 0,
actualStartTime: -1,
selfBaseDuration: 0,
treeBaseDuration: 0,
_debugSource: [Object],
_debugOwner: [FiberNode],
_debugNeedsRemount: false,
_debugHookTypes: null
},
_debugNeedsRemount: false,
_debugHookTypes: null
},
'__reactProps$ryal2e46zro': {
id: 'clientName',
type: 'text',
value: '',
onChange: [Function: onChange],
className: 'form-control mb-2',
'data-testid': 'client-name'
},
_wrapperState: { initialChecked: undefined, initialValue: '', controlled: true },
'__reactEvents$ryal2e46zro': Set(1) { 'invalid__bubble' },
value: [Getter/Setter],
_valueTracker: {
getValue: [Function: getValue],
setValue: [Function: setValue],
stopTracking: [Function: stopTracking]
},
setSelectionRange: [Function: intercept] {
[Symbol(Interceptor for programmatical calls)]: Symbol(Interceptor for programmatical calls)
},
selectionStart: [Getter/Setter],
selectionEnd: [Getter/Setter],
select: [Function: intercept] {
[Symbol(Interceptor for programmatical calls)]: Symbol(Interceptor for programmatical calls)
},
setRangeText: [Function: intercept] {
[Symbol(Interceptor for programmatical calls)]: Symbol(Interceptor for programmatical calls)
},
[Symbol(Last check for pointer-events)]: {
'1': {},
'2': {},
result: { pointerEvents: 'auto', tree: [Array] }
},
[Symbol(Displayed selection in UI)]: { anchorOffset: 0, focusOffset: 0 },
[Symbol(Node prepared with document state workarounds)]: Symbol(Node prepared with document state workarounds),
[Symbol(Initial value to compare on blur)]: '',
[Symbol(Displayed value in UI)]: undefined,
[Symbol(Track programmatic changes for React workaround)]: undefined
}
如你所见,value
的财产仍然是"......所以也许await user.type()
号公路根本不管用
我只想断言,当用户输入某些内容时,输入值会正确地更新.
为什么这个不能通过?
谢谢!
感谢@大卫团的回答,更新了W/答案. 最终测试文件编码:
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { MockedProvider } from '@apollo/client/testing';
import CreateClientForm from '../../src/components/CreateClientForm';
import { useState } from 'react';
// Create a mock Parent component:
const MockParentComponent = () => {
const [clientName, setClientName] = useState('');
const [clientEmail, setClientEmail] = useState('');
const [clientPhone, setClientPhone] = useState('');
return (
<MockedProvider>
<CreateClientForm
clientName={clientName}
setClientName={setClientName}
clientEmail={clientEmail}
setClientEmail={setClientEmail}
clientPhone={clientPhone}
setClientPhone={setClientPhone}
/>
</MockedProvider>
);
};
beforeEach(() => {
render(
<MockedProvider>
<MockParentComponent />
</MockedProvider>
);
});
describe('Create Client Form In Modal', () => {
it('renders the correct initial input values', () => {
expect(screen.getByLabelText('Client Name:')).toBeInTheDocument();
expect(screen.getByLabelText('Client Email:')).toBeInTheDocument();
expect(screen.getByLabelText('Client Phone:')).toBeInTheDocument();
});
it('updates state values as a user types', async () => {
const user = userEvent.setup();
const nameInput = screen.getByRole('textbox', {
name: 'Client Name:',
}) as HTMLInputElement;
// Check initial value
expect(nameInput.value).toEqual('');
// Simulate user typing
await user.type(nameInput, 'Johnny Bravo');
console.log(
screen.getByRole('textbox', {
name: 'Client Name:',
})
);
// Verify updated value after change event
expect(nameInput.value).toEqual('Johnny Bravo');
});
});