【问题标题】:Why do these sinon stubs resolve to undefined?为什么这些 sinon 存根解析为未定义?
【发布时间】:2020-11-27 17:30:43
【问题描述】:

我为以下代码编写了一个单元测试,并使用 Sinon(更具体地说:sinon-chrome,一个过时但仍在运行的库,适用于我的用例)对浏览器方法(阅读:web-extension API)进行了存根。

/**
 * Returns an array of languages based on getAcceptLanguages and getUILanguage to use as defaults
 * for when no saved languages exist in browser storage.
 *
 * @memberof Helpers
 * @returns {array} Array of language codes i.e. ['en-US', 'fr']
 */
async function getDefaultLanguages () {
  const acceptedLanguages = await browser.i18n.getAcceptLanguages()
  const uiLanguage = browser.i18n.getUILanguage()

  return [uiLanguage].concat(acceptedLanguages)
}

单元测试:

const sinon = require('sinon')
const browser = require('sinon-chrome/extensions')
const { assert } = require('chai')
const helpers = require('../src/helpers')

// helpers that rely on the web-extension API (will need to be mocked)
describe('Helpers: Web-Extension API', function () {
  const { getDefaultLanguages } = helpers

  let languages

  before(async function () {
    global.browser = browser // need to patch global browser with mocked api
    browser.menus = browser.contextMenus // sinon-chrome doesn't wrap this method as it should
    
    sinon.stub(browser.i18n, 'getAcceptLanguages').resolves(['de-de', 'en-au'])
    sinon.stub(browser.i18n, 'getUILanguage').returns('en-en')

    languages = await getDefaultLanguages()
  })

  it('asserts that getDefaultLanguages() returns an array of strings', function () {
    assert.isTrue(languages.every(x => typeof x === 'string'))
  })

  it('asserts that getDefaultLanguages() includes UI and i18n languages', function () {
    assert.sameMembers(languages, ['de-de', 'en-en', 'en-au'])
  })
})

由于两个存根方法返回未定义,测试失败,但Sinon docs state 很明显stub.resolves(value)

使存根返回一个 Promise,该 Promise 解析为提供的 价值。

在构造 Promise 时,sinon 使用 Promise.resolve 方法。 你有责任在环境中提供一个 polyfill 不提供承诺。 Promise 库可以使用 使用Promise 方法。

由于节点已内置 Promise 支持,我希望上述存根使用指定的值(区域设置字符串数组和区域设置字符串)进行解析,但两者都解析/返回未定义。

不胜感激!

【问题讨论】:

    标签: javascript node.js mocha.js chai sinon


    【解决方案1】:

    结果是 sinon-chrome,无论出于何种原因,都需要在运行时和运行测试之前注册“i18n”插件。

    为什么 web-extensions API 的这个特定部分的实现方式与所有其他 mock 不同仍然是个谜,但添加两行解决了问题并允许 sinon 存根按预期工作:

    const sinon = require('sinon')
    const browser = require('sinon-chrome/extensions')
    const I18nPlugin = require('sinon-chrome/plugins').I18nPlugin // I18n plugin constructor
    const { assert } = require('chai')
    const helpers = require('../src/helpers')
    
    // helpers that rely on the web-extension API (will need to be mocked)
    describe('Helpers: Web-Extension API', function () {
      const { getDefaultLanguages } = helpers
    
      let languages
    
      before(async function () {
        global.browser = browser // need to patch global browser with mocked api
        browser.menus = browser.contextMenus // sinon-chrome doesn't wrap this method as it should
        browser.registerPlugin(new I18nPlugin()) // register the plugin on browser instance
        
        sinon.stub(browser.i18n, 'getAcceptLanguages').resolves(['de-de', 'en-au'])
        sinon.stub(browser.i18n, 'getUILanguage').returns('en-en')
    
        languages = await getDefaultLanguages()
      })
    })
    

    如果你试图窥探一个不存在的对象属性,或者如果给定的属性不是一个函数,Sinon-chrome 会抛出一个类型错误。不幸的是,如果尝试存根不存在的对象属性,它不会抛出任何错误,只会返回 undefined,这似乎是一个糟糕的设计选择。

    这就是为什么 returns()resolves() 存根在原始代码中都返回 undefined。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-14
      • 2017-06-29
      • 1970-01-01
      • 2022-01-18
      • 1970-01-01
      • 2011-05-05
      • 2018-08-19
      相关资源
      最近更新 更多