【问题标题】:Jasmine spyOn doesn't stub the functionJasmine spyOn 不会存根函数
【发布时间】:2016-10-23 17:17:51
【问题描述】:

我正在尝试使用 stub 函数

import * as tooltip from './tooltip';

describe('tooltip', () => {
  it('should ...', () => {
    // arrange
    spyOn(tooltip, 'createTooltip');
    ...

    tooltip.tooltip.inserted(...); // calls createTooltip inside

    ...
    expect(tooltip.createTooltip).toHaveBeenCalledWith(...); // assert called with
  });
});

但是当inserted被调用时,createTooltip的真正实现被调用了,并且assert正在抛出错误信息:

Expected spy createTooltip to have been called with ... but it was never called.

更新 1: 完整的工具提示指令代码:

import $ from "jquery";
import classes from '../../css/directives/tooltip.scss';

const TOOLTIP_CLASS = classes.tooltip;
const TOOLTIP_ARROW_CLASS = classes.arrow;
const TOOLTIP_ARROW_BORDER_WIDTH = 5;
const TOOLTIP_DEFAULT_MARGIN = 2;
const TOOLTIP_DEFAULT_BACKGROUND_COLOR = 'rgba(0, 0, 0, .8)';
const TOOLTIP_DEFAULT_FADE_SPEED = 'slow';
const TOOLTIP_DEFAULT_POSITION = 'right';
const POSITION_FN = {
  top: positionTooltipToTop,
  left: positionTooltipToLeft,
  right: positionTooltipToRight,
  bottom: positionTooltipToBottom
};

export const tooltip = {
  inserted: (el, binding) => {
    const $body = $('body');
    const $el = $(el);
    const $tooltip = createTooltip($body, $el, binding);
    $el.mouseenter(() => {
      $tooltip
        .stop()
        .hide()
        .appendTo($body)
        .fadeIn(TOOLTIP_DEFAULT_FADE_SPEED || binding.value.fade);
    });
    $el.mouseleave(() => {
      $tooltip
        .stop()
        .fadeOut(TOOLTIP_DEFAULT_FADE_SPEED || binding.value.fade, () => {
          $tooltip.detach();
        });
    });
  }
};

export function createTooltip($body, $el, binding) {
  console.log('in function createTooltip');
  const $arrow = createTooltipArrow();
  const $tooltip = $(document.createElement('span'));
  $tooltip.html(binding.value.message);
  $tooltip.append($arrow);
  if (binding.value.color) {
    $tooltip.css('color', binding.value.color);
  }
  if (binding.value.backgroundColor) {
    $tooltip.css('background-color', binding.value.backgroundColor);
  }
  $tooltip.addClass(TOOLTIP_CLASS);
  $body.append($tooltip);
  POSITION_FN[binding.value.position || TOOLTIP_DEFAULT_POSITION]($el, $tooltip, $arrow, binding);
  $tooltip.detach();
  return $tooltip;
}

export function createTooltipArrow() {
  const $arrow = $(document.createElement('span'));
  $arrow.addClass(TOOLTIP_ARROW_CLASS);
  return $arrow;
}

export function positionTooltipToTop($el, $tooltip, $arrow, binding) {
  $tooltip.css({
    top: $el.offset().top - $tooltip.outerHeight() - (TOOLTIP_ARROW_BORDER_WIDTH + (binding.value.margin || TOOLTIP_DEFAULT_MARGIN)),
    left: $el.offset().left + ($el.outerWidth() / 2) - ($tooltip.outerWidth() / 2)
  });
  $arrow.css({
    top: '100%',
    left: '50%',
    marginLeft: -1 * TOOLTIP_ARROW_BORDER_WIDTH,
    borderTopColor: binding.value.backgroundColor || TOOLTIP_DEFAULT_BACKGROUND_COLOR
  });
}

export function positionTooltipToRight($el, $tooltip, $arrow, binding) {
  $tooltip.css({
    top: $el.offset().top + ($el.outerHeight() / 2) - ($tooltip.outerHeight() / 2),
    left: $el.offset().left + $el.outerWidth() + TOOLTIP_ARROW_BORDER_WIDTH + (binding.value.margin || TOOLTIP_DEFAULT_MARGIN)
  });
  $arrow.css({
    top: '50%',
    right: '100%',
    marginTop: -1 * TOOLTIP_ARROW_BORDER_WIDTH,
    borderRightColor: binding.value.backgroundColor || TOOLTIP_DEFAULT_BACKGROUND_COLOR
  });
}

export function positionTooltipToLeft($el, $tooltip, $arrow, binding) {
  $tooltip.css({
    top: $el.offset().top + ($el.outerHeight() / 2) - ($tooltip.outerHeight() / 2),
    left: $el.offset().left - $tooltip.outerWidth() - (TOOLTIP_ARROW_BORDER_WIDTH + (binding.value.margin || TOOLTIP_DEFAULT_MARGIN))
  });
  $arrow.css({
    top: '50%',
    left: '100%',
    marginTop: -1 * TOOLTIP_ARROW_BORDER_WIDTH,
    borderLeftColor: binding.value.backgroundColor || TOOLTIP_DEFAULT_BACKGROUND_COLOR
  });
}

export function positionTooltipToBottom($el, $tooltip, $arrow, binding) {
  $tooltip.css({
    top: $el.offset().top + $el.outerHeight() + TOOLTIP_ARROW_BORDER_WIDTH + (binding.value.margin || TOOLTIP_DEFAULT_MARGIN),
    left: $el.offset().left + ($el.outerWidth() / 2) - ($tooltip.outerWidth() / 2)
  });
  $arrow.css({
    bottom: '100%',
    left: '50%',
    marginLeft: -1 * TOOLTIP_ARROW_BORDER_WIDTH,
    borderBottomColor: binding.value.backgroundColor || TOOLTIP_DEFAULT_BACKGROUND_COLOR
  });
}

【问题讨论】:

  • 能否请您发布tooltip 模块代码。
  • @dm03514 更新了操作,也没有说我已经导出了所有辅助函数,所以我以后也可以测试它们。如果您有更好的方法将它们保密,请告诉我。
  • dmicantech.blogspot.com/2015/11/…我在一篇博文中说明了这个问题
  • @dm03514 我读过它,虽然我同意 di 在大多数情况下都可以,但我的情况并非如此。我有一个为 vue.js 框架编写的指令,如果我要让它注入 createTooltip() 而不是仅仅用作辅助函数,我将不得不这样做,所以我的指令的每个用户都必须通过另一个它的价值在于它是一个处理 DOM 的函数,在我看来这真的很糟糕,因为用户在使用 such 指令时不应该关心这一点。你认为还有其他可行的解决方案吗?

标签: javascript unit-testing jasmine stub


【解决方案1】:

我的解决方案:

我在构建过程中使用babel-plugin-rewire,并且我能够在没有 di 的情况下测试指令,这对我来说没有多大意义。

测试代码:

import $ from 'jquery';

import { tooltip, __RewireAPI__ as TooltipRewireAPI } from './tooltip';

describe('tooltip', () => {
  const createTooltipSpy = jasmine.createSpy('createTooltipSpy');
  beforeEach(() => {
    TooltipRewireAPI.__Rewire__('createTooltip', createTooltipSpy);
  });
  afterEach(() => {
    TooltipRewireAPI.__ResetDependency__('createTooltip');
  });
  it('should have mouseenter and mouseleave event listeners', () => {
    const $body = $('body');
    const el = document.createElement('div');
    const binding = {};
    const $el = $(el);

    tooltip.inserted(el, binding);

    expect(createTooltipSpy).toHaveBeenCalledWith($body, $el, binding);
    expect($el.mouseenter).toEqual(jasmine.any(Function));
    expect($el.mouseleave).toEqual(jasmine.any(Function));
  });
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多