【问题标题】:Simulate a button click in Jest在 Jest 中模拟按钮单击
【发布时间】:2026-02-03 05:25:01
【问题描述】:

模拟按钮点击似乎是一个非常简单/标准的操作。然而,我无法让它在 Jest.js 测试中工作。

这是我尝试过的(也使用 jQuery),但它似乎没有触发任何事情:

import { mount } from 'enzyme';

page = <MyCoolPage />;
pageMounted = mount(page);

const button = pageMounted.find('#some_button');
expect(button.length).toBe(1); // It finds it alright
button.simulate('click'); // Nothing happens

【问题讨论】:

  • 你怎么知道它什么也没做?您接下来检查什么以查看是否发生了按钮点击?
  • 好问题。我希望出现错误字段: const field = pageMounted.find('#notification');期望(field.length).toBe(1);
  • 人力资源部。您是否在运行 onClick 的函数中添加了 console.warn 以查看它是否在 Jest 控制台中触发?
  • 能否请您添加MyCoolPage 组件的代码,否则很难弄清楚实际问题是什么。
  • 谢谢你们的提示。我发现了我的问题,感谢您的提问。我基本上用一个简单的按钮做了一个小测试,它起作用了: MyCoolPage = ( );然后我意识到我的按钮属于redux-form,所以它没有onClick,而是onSubmit,所以添加button.simulate('submit');解决了这个问题。再次感谢您的反馈!

标签: javascript reactjs testing jestjs enzyme


【解决方案1】:

#1 使用 Jest

这就是我使用 Jest 模拟回调函数来测试点击事件的方式:

import React from 'react';
import { shallow } from 'enzyme';
import Button from './Button';

describe('Test Button component', () => {
  it('Test click event', () => {
    const mockCallBack = jest.fn();

    const button = shallow((<Button onClick={mockCallBack}>Ok!</Button>));
    button.find('button').simulate('click');
    expect(mockCallBack.mock.calls.length).toEqual(1);
  });
});

我还使用了一个名为 enzyme 的模块。 Enzyme 是一个测试实用程序,可以更轻松地断言和选择您的 React 组件

#2 使用诗乃

此外,您还可以使用另一个名为 Sinon 的模块,它是 JavaScript 的独立测试间谍、存根和模拟。看起来是这样的:

import React from 'react';
import { shallow } from 'enzyme';
import sinon from 'sinon';

import Button from './Button';

describe('Test Button component', () => {
  it('simulates click events', () => {
    const mockCallBack = sinon.spy();
    const button = shallow((<Button onClick={mockCallBack}>Ok!</Button>));

    button.find('button').simulate('click');
    expect(mockCallBack).toHaveProperty('callCount', 1);
  });
});

#3 使用你自己的间谍

最后,您可以制作自己的幼稚间谍(除非您有正当理由,否则我不推荐这种方法)。

function MySpy() {
  this.calls = 0;
}

MySpy.prototype.fn = function () {
  return () => this.calls++;
}

it('Test Button component', () => {
  const mySpy = new MySpy();
  const mockCallBack = mySpy.fn();

  const button = shallow((<Button onClick={mockCallBack}>Ok!</Button>));

  button.find('button').simulate('click');
  expect(mySpy.calls).toEqual(1);
});

【讨论】:

  • 感谢萨满的详细解答!当您可以将 onClick 方法直接传递到您正在测试的组件中时,这非常有用,我将使用您的代码作为参考:)。我认为在我的示例中,虽然我无法真正传递 onClick,但我必须依靠其他线索才能知道按钮已被点击。
  • 这实际上测试了什么?
  • 我有一个按钮,点击时会调用我的handleClick 方法。我如何测试单击按钮时实际调用了handleClick
  • @Saman Shafigh 如果让我们说按钮嵌套了两层,这将如何工作?所以点击处理程序是从第一个组件传递到第二个组件,然后最终传递到按钮。
  • 以上所有示例不都只是测试 HTML 是否有效吗?即,如果我创建一个按钮并为其分配一个点击事件,它会调用该点击事件吗?它不是对特定于我们代码的任何内容进行单元测试。
【解决方案2】:

已弃用已接受答案中的解决方案

#4 直接调用 props

Enzyme simulation 应该在版本 4 中被删除。主要维护者建议直接调用 prop 函数,这就是模拟内部所做的。一种解决方案是直接测试调用这些道具是否正确;或者您可以模拟实例方法,测试 prop 函数是否调用它们,然后对实例方法进行单元测试。

你可以调用click,例如:

wrapper.find('Button').prop('onClick')() 

或者

wrapper.find('Button').props().onClick() 

关于弃用的信息: Deprecation of .simulate() #2173

【讨论】:

  • 上一个答案?还是不止一个(哪些?)?
  • @PeterMortensen 我已经澄清了答案。接受的答案是使用酶模拟,这将被弃用。
  • 您可能需要在其中之一之后致电wrapper.update(),因为酶可能无法注意到发生了变化。
  • 如果按钮没有onClick 属性怎么办?例如type="submit" 内的&lt;form /&gt; 按钮?是的,可以在表格上致电onSubmit - 但这并不理想。用户将点击按钮,这就是您要测试的内容。
  • 万一有人遇到同样的问题,如果你必须处理这个事件,这可能很有用:``` lang-js act(() => { component.root.findByType(' button').props.onClick({ preventDefault: jest.fn(), stopPropagation: jest.fn(), }); }); ```
【解决方案3】:

使用 Jest,您可以这样做:

test('it calls start logout on button click', () => {
    const mockLogout = jest.fn();
    const wrapper = shallow(<Component startLogout={mockLogout}/>);
    wrapper.find('button').at(0).simulate('click');
    expect(mockLogout).toHaveBeenCalled();
});

【讨论】:

  • 在您的测试中创建一个完整的按钮并在单击时使用模拟回调然后在测试中单击该按钮有什么价值?就像我见过的大多数测试示例一样,当您执行此操作时,您甚至没有测试任何实际代码。
  • @JeremyMoritz 这就是为什么我不理解单元测试的要点或逻辑。
【解决方案4】:

Testing-library 使用click function 让您轻松完成这项工作。

它是 user-event 库的一部分,可用于每个 dom 环境(react、jsdom、浏览器……)

文档中的示例:

import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'

test('click', () => {
  render(
    <div>
      <label htmlFor="checkbox">Check</label>
      <input id="checkbox" type="checkbox" />
    </div>,
  )

  userEvent.click(screen.getByText('Check'))
  expect(screen.getByLabelText('Check')).toBeChecked()
})

【讨论】:

    【解决方案5】:

    您可以使用类似这样的方式来调用单击时编写的处理程序:

    import { shallow } from 'enzyme'; // Mount is not required
    
    page = <MyCoolPage />;
    pageMounted = shallow(page);
    
    // The below line will execute your click function
    pageMounted.instance().yourOnClickFunction();
    

    【讨论】:

      【解决方案6】:

      除了同级 cmets 中建议的解决方案之外,您可以稍微更改您的测试方法,而不是一次测试整个页面(使用深层子组件树),但要这样做隔离组件测试。这将简化onClick() 和类似事件的测试(参见下面的示例)。

      我们的想法是一次只测试一个组件,而不是全部一起测试。在这种情况下,所有子组件都将使用jest.mock() 函数进行模拟。

      下面是如何使用Jestreact-test-renderer 在隔离的SearchForm 组件中测试onClick() 事件的示例。

      import React from 'react';
      import renderer from 'react-test-renderer';
      import { SearchForm } from '../SearchForm';
      
      describe('SearchForm', () => {
        it('should fire onSubmit form callback', () => {
          // Mock search form parameters.
          const searchQuery = 'kittens';
          const onSubmit = jest.fn();
      
          // Create test component instance.
          const testComponentInstance = renderer.create((
            <SearchForm query={searchQuery} onSearchSubmit={onSubmit} />
          )).root;
      
          // Try to find submit button inside the form.
          const submitButtonInstance = testComponentInstance.findByProps({
            type: 'submit',
          });
          expect(submitButtonInstance).toBeDefined();
      
          // Since we're not going to test the button component itself
          // we may just simulate its onClick event manually.
          const eventMock = { preventDefault: jest.fn() };
          submitButtonInstance.props.onClick(eventMock);
      
          expect(onSubmit).toHaveBeenCalledTimes(1);
          expect(onSubmit).toHaveBeenCalledWith(searchQuery);
        });
      });
      

      【讨论】:

        【解决方案7】:

        我需要自己测试一下按钮组件。这些测试对我有用;-)

        import { shallow } from "enzyme";
        import * as React from "react";
        import Button from "../button.component";
        
        describe("Button Component Tests", () => {
            it("Renders correctly in DOM", () => {
                shallow(
                    <Button text="Test" />
                );
            });
            it("Expects to find button HTML element in the DOM", () => {
                const wrapper = shallow(<Button text="test"/>)
                expect(wrapper.find('button')).toHaveLength(1);
            });
        
            it("Expects to find button HTML element with className test in the DOM", () => {
                const wrapper = shallow(<Button className="test" text="test"/>)
                expect(wrapper.find('button.test')).toHaveLength(1);
            });
        
            it("Expects to run onClick function when button is pressed in the DOM", () => {
                const mockCallBackClick = jest.fn();
                const wrapper = shallow(<Button onClick={mockCallBackClick} className="test" text="test"/>);
                wrapper.find('button').simulate('click');
                expect(mockCallBackClick.mock.calls.length).toEqual(1);
            });
        });
        

        【讨论】: