You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Jest测试中使用jQuery检测元素可见性的问题排查

问题根源分析

你遇到的问题主要来自三个核心原因:

  1. JSDOM不支持CSS动画执行
    Jest默认使用JSDOM作为DOM测试环境,它仅模拟DOM结构,不会像真实浏览器那样运行CSS动画或过渡效果。jQuery的toggle("fast")是带渐进式动画的方法,在JSDOM中动画完全不会被执行,元素样式永远停留在初始状态,导致is(":visible")始终返回初始值。

  2. 动画的异步性未被处理
    即使在真实浏览器中,toggle("fast")也是异步执行的,你在点击后立即断言时,动画还未完成,元素状态尚未改变,断言自然会失败。JSDOM跳过动画的特性让这个问题被进一步放大。

  3. jQuery:visible选择器的环境差异
    jQuery的:visible判断依赖于元素的offsetWidth、offsetHeight等布局属性,JSDOM对这些属性的计算逻辑和真实浏览器存在差异,尤其是在动画未执行的情况下,这些属性不会被正确更新。


解决方案

下面提供几种靠谱的解决方法,按推荐程度排序:

方案1:禁用jQuery动画(最推荐)

jQuery提供了全局开关$.fx.off,设置为true后,所有动画方法(toggle、show、hide、animate)都会跳过动画,直接将元素设置到最终状态。这样在测试中,元素状态会立即更新,is(":visible")就能正确判断了。

修改你的测试代码,在beforeEach中添加这个开关:

jest.dontMock('jquery');
jest.dontMock('../toggle.js');
$ = jQuery = require('jquery');

describe('toggle.js', function() {
  var sut = require('../toggle.js');

  // 在所有测试前禁用动画,让状态立即生效
  beforeEach(() => {
    $.fx.off = true;
  });

  function givenFields(formFields) {
    document.documentElement.innerHTML = `
    <html><body><form>
    <div id="field">hi</div>
    ${formFields}
    </form></body></html>
    `;
    sut.initialize();
  }

  it('toggles the field', function() {
    givenFields(`<input type='checkbox' id="toggle">toggle</input>`);
    var initiallyVisible = $("#field").is(":visible");
    $('#toggle').click();
    expect($("#field").is(":visible")).toEqual(!initiallyVisible);
  });

  // 注意:你的toggle.js中需要补充show按钮的事件绑定,否则这个测试还是会失败
  // 比如在toggle.js中添加:
  // $('form').on('change', 'input#show[type="checkbox"]', function() {
  //   $('#field').show();
  // });
  it('shows the field', function() {
    givenFields(`<input type='checkbox' id="show">show</input>`);
    $('#show').click();
    expect($("#field").is(":visible")).toEqual(true);
  });
});

方案2:使用Jest定时器模拟动画完成

如果需要保留动画逻辑的测试,可以利用Jest的假定时器——因为jQuery的动画是基于setTimeout实现的,我们可以快进所有定时器来模拟动画完成:

it('toggles the field with animation simulation', function() {
  givenFields(`<input type='checkbox' id="toggle">toggle</input>`);
  jest.useFakeTimers(); // 启用假定时器
  var initiallyVisible = $("#field").is(":visible");
  
  $('#toggle').click();
  jest.runAllTimers(); // 快进所有定时器,让动画"完成"
  
  expect($("#field").is(":visible")).toEqual(!initiallyVisible);
  jest.useRealTimers(); // 还原真实定时器
});

不过这个方案不如禁用动画稳定,因为不同版本的jQuery动画使用的定时器逻辑可能有变化。

方案3:直接检查元素样式属性(替代:visible)

如果你不想修改动画行为,可以直接检查元素的display属性或布局属性,避免依赖jQuery的:visible选择器:

// 检查display属性
expect($("#field").css("display")).not.toBe("none");

// 或者检查offsetHeight(元素可见时offsetHeight>0)
expect($("#field")[0].offsetHeight).toBeGreaterThan(0);

这些方法更直接,在JSDOM环境下的兼容性更好。


额外注意点

你的shows the field测试失败,还有一个可能的原因是toggle.js中没有绑定#show复选框的change事件,你需要在toggle.js中补充对应的逻辑:

// 在toggle.js中添加show按钮的处理
$('form').on('change', 'input#show[type="checkbox"]', function() {
  $('#field').show();
});

// 同理,hide按钮的处理
$('form').on('change', 'input#hide[type="checkbox"]', function() {
  $('#field').hide();
});

内容的提问来源于stack exchange,提问作者Roman-Stop RU aggression in UA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:17:18