Reactjs 未从react测试库中的.toHaveBeenCalledTimes()获取预期结果

Reactjs 未从react测试库中的.toHaveBeenCalledTimes()获取预期结果,reactjs,unit-testing,jestjs,react-testing-library,Reactjs,Unit Testing,Jestjs,React Testing Library,无论如何,尝试测试函数在被激发后是否被调用。当我从该函数中获得一个console.log时,firevent正在工作。但是.toHaveBeenCalledTimes(1)返回0。我错过了什么 如果我在父级中有handleLoginSubmit函数,并将其作为一个道具传递给子级,那么在测试中,一切都通过了。但是如果它在同一个组件中,它就会失败。如果有任何意义,请使用typescript 这是经过测试的 import React, { FC } from 'react'; type Event

无论如何,尝试测试函数在被激发后是否被调用。当我从该函数中获得一个
console.log
时,
firevent
正在工作。但是
.toHaveBeenCalledTimes(1)
返回0。我错过了什么

如果我在父级中有
handleLoginSubmit
函数,并将其作为一个道具传递给子级,那么在测试中,一切都通过了。但是如果它在同一个组件中,它就会失败。如果有任何意义,请使用typescript

这是经过测试的

import React, { FC } from 'react';

type Event = React.FormEvent<HTMLFormElement>;

interface Login {
  handleLoginSubmit?: (event: Event) => React.ReactNode;
}

const Login: FC<Login> = () => {
  const handleLoginSubmit = (_event: Event) => {
    console.log('Firing' ); // This is logged
  };

  return (
    <form data-testid='form' onSubmit={(event) => handleLoginSubmit(event)}>
      <input data-testid='email'/>
      <input data-testid='password'/>
      <button data-testid='login-button'>login</button>
    </form>
  );
};

export default Login;

您不能断言是否要直接调用
handleLoginSubmit
函数。因为它是在
Login
SFC的私有范围内定义的。您无法模拟或监视此函数,因为您无法访问它。所以,你需要间接地测试它。由于您在此函数中使用的是
console.log
,因此我们可以监视
console.log
。如果调用了它,则表示调用了
handleLoginSubmit
函数

例如

index.tsx

import React,{FC}来自“React”;
类型事件=React.FormEvent;
界面登录{
handleLoginSubmit?:(事件:事件)=>React.ReactNode;
}
常量登录:FC=()=>{
const handleLoginSubmit=(\u event:event)=>{
控制台日志(“点火”);
};
返回(
handleLoginSubmit(事件)}>
登录
);
};
导出默认登录;
index.spec.tsx

从“@testing library/react”导入{render,firevent}”;
从“/”导入登录名;
从“React”导入React;
它(“应该处理ClickEvents”,()=>{
const logSpy=jest.spyOn(控制台,“日志”);
const{getByTestId}=render();
expect(getByTestId(“登录按钮”)).toBeTruthy();
fireEvent.submit(getByTestId(“表单”);
预期(logSpy)。已被催收时间(1);
});
100%覆盖率的单元测试结果:

PASS src/stackoverflow/59162138/index.spec.tsx
✓ 应处理ClickEvents(42毫秒)
console.log node_modules/jest mock/build/index.js:860
发射
-----------|----------|----------|----------|----------|-------------------|
文件|%Stmts |%Branch |%Funcs |%Line |未覆盖行|s|
-----------|----------|----------|----------|----------|-------------------|
所有文件| 100 | 100 | 100 | 100 ||
index.tsx | 100 | 100 | 100 | 100 ||
-----------|----------|----------|----------|----------|-------------------|
测试套件:1个通过,共1个
测试:1项通过,共1项
快照:共0个
时间:3.987秒,估计9秒

源代码:

这里的问题是,您正在将
handleLoginSubmit
赋值给一个变量,但是其中的变量不同,尽管共享相同的名称(它在组件上的范围)。如果希望它工作,请将
handleLoginSubmit
作为
onSubmit
道具传递给组件,然后像
render()
那样模拟道具,它将被称为
onSubmit
道具传递是什么意思?您的意思是将它从父级传递到
Login
组件吗?我不想这样做,因为我想在该组件中调用函数。啊,好的,所以当测试console.log(或其他控制台输出)时,我们需要在spy中指定它?所以,当测试lets say state时,我们需要一个间谍,它接受react,state或类似的参数?谢谢,顺便说一句!测试组件的状态,不要测试SFC中定义的功能的实现细节。这意味着函数(挂钩、事件处理程序等)将更改组件的状态。您应该声明组件的状态。好的,因此如果我想测试forms input changeevent,我不应该测试处理状态/事件的函数,而应该测试DOM中的输出/实际输入字段中的输出?@GameAtrix Yes。此外,如果更改事件处理程序是在SFC的私有范围中定义的,则无法访问它。所以,你不能添加间谍并模仿它。您需要测试组件的更新状态/属性(实际输入字段中的输出)
  it('should handle ClickEvents', () => {

    const handleLoginSubmit = jest.fn();

    const { getByTestId } = render(<Login/>);

    expect(getByTestId('login-button')).toBeTruthy();

    fireEvent.submit(getByTestId('form'));

    expect(handleLoginSubmit).toHaveBeenCalledTimes(1);

  });
 ● Login page › should handle ClickEvents

    expect(jest.fn()).toHaveBeenCalledTimes(expected)

    Expected number of calls: 1
    Received number of calls: 0

      32 |     fireEvent.submit(getByTestId('form'));
      33 | 
    > 34 |     expect(handleLoginSubmit).toHaveBeenCalledTimes(1);
         |                               ^
      35 | 
      36 |   });
      37 | });

      at Object.it (__tests__/components/Login.test.tsx:34:31)