测试抛出错误的承诺和同步功能

Testing promises and sync functions that throw errors

我正在尝试同时构建和测试功能。测试是有道理的,理论上我喜欢它,但当它归结为它时,它总是背后的痛苦。

我有一个函数,它接受一个字符串并在出现问题时抛出错误,如果一切顺利,它将 return 原始 text 参数,因此 truthy 值,如果不是,它应该被它本身或本身作为承诺的承诺所捕获。

这是测试/我真正想做的(这是行不通的)。

var main = require("./index.js")
var Promise = require("bluebird")
var mocha = require("mocha")
var chai = require("chai")
var chaiPromise = require("chai-as-promised")
chai.use(chaiPromise)

var shouldThrow = [
  "random", // invalid non-flag
  "--random", // invalid flag
  "--random string", //invalid flag
  "--wallpaper", // invalid flag w/ match
  "--notify", // invalid flag w/ match
  "wallpaper", // valid non-flag missing option(s) image
  "wallpaper image.jpg" // invalid flag value
  "wallpaper http://cdn.shopify.com/s/files/1/0031/5352/t/28/assets/holstee-calendar-2015-03-flow-desktop.jpg?12375621748379006621", // invalid flag value
  "wallpaper //cdn.shopify.com/s/files/1/0031/5352/t/28/assets/favicon.ico?12375621748379006621", // invalid flag value
  "wallpaper http://cdn.shopify.com/s/files/1/0031/5352/t/28/assets/favicon.ico?12375621748379006621",  // invalid flag value
  "wallpaper https://cdn.shopify.com/s/files/1/0031/5352/t/28/assets/favicon.ico?12375621748379006621",  // invalid flag value
  "wallpaper https://cdn.example.com/s/files/1/0031/5352/files/holstee_logo_2.png?4803", // invalid flag value
  "wallpaper https://cdn.shopify.com/s/files/1/0031/5352/t/28/assets/holstee-calendar-2015-03-flow-desktop.jpg?12375621748379006621 --queue",  // invalid flag value
  "wallpaper https://cdn.shopify.com/s/files/1/0031/5352/t/28/assets/holstee-calendar-2015-03-flow-desktop.jpg?12375621748379006621 --queue "+moment().subtract(1, "month").format("YYYY-MM-DD-HH-mm"),  // invalid flag value
  "wallpaper https://cdn.shopify.com/s/files/1/0031/5352/t/28/assets/holstee-calendar-2015-03-flow-desktop.jpg?12375621748379006621 --queue "+moment().add(1, "month").format("YY-MM-DD-HH"), // invalid flag value
  "wallpaper --image http://cdn.shopify.com/s/files/1/0031/5352/t/28/assets/holstee-calendar-2015-03-flow-desktop.jpg?12375621748379006621", // invalid flag value not https
  "wallpaper --image //cdn.shopify.com/s/files/1/0031/5352/t/28/assets/favicon.ico?12375621748379006621", // invalid flag no protocol
  "wallpaper --image http://cdn.shopify.com/s/files/1/0031/5352/t/28/assets/favicon.ico?12375621748379006621",  // invalid flag value not https
  "wallpaper --image https://cdn.shopify.com/s/files/1/0031/5352/t/28/assets/favicon.ico?12375621748379006621",  // invalid flag value not valid image
  "wallpaper --image https://cdn.example.com/s/files/1/0031/5352/files/holstee_logo_2.png?4803", // invalid flag image not found
  "wallpaper --image https://cdn.shopify.com/s/files/1/0031/5352/t/28/assets/holstee-calendar-2015-03-flow-desktop.jpg?12375621748379006621 --queue",  // invalid subflag queue missing value
  "wallpaper --image https://cdn.shopify.com/s/files/1/0031/5352/t/28/assets/holstee-calendar-2015-03-flow-desktop.jpg?12375621748379006621 --queue "+moment().subtract(1, "month").format("YYYY-MM-DD-HH-mm"),  // invalid subflag queue date value is past
  "wallpaper --image https://cdn.shopify.com/s/files/1/0031/5352/t/28/assets/holstee-calendar-2015-03-flow-desktop.jpg?12375621748379006621 --queue "+moment().add(1, "month").format("YY-MM-DD-HH"), // invalid subflag queue date value format
  "--wallpaper --image https://cdn.shopify.com/s/files/1/0031/5352/t/28/assets/holstee-calendar-2015-03-flow-desktop.jpg?12375621748379006621", //no action non-flag
  "--wallpaper --image https://cdn.shopify.com/s/files/1/0031/5352/t/28/assets/holstee-calendar-2015-03-flow-desktop.jpg?12375621748379006621 --queue "+moment().add(1, "month").format("YYYY-MM-DD-HH-mm"), //no action non-flag
  "notify", // valid non-flag missing option(s) message, open
  'notify --message "Hello world"', // valid flag missing params open
  'notify --open "https://www.holstee.com"', // valid flag missing params message
  'notify --message "Hello world" --open "http://www.holstee.com"', // invalid subflag value `open` should be https
  'notify --message "Hello world" --open "https://www.holstee.com" --queue', // invalid subflag queue missing value
  'notify --message "Hello world" --open "https://www.holstee.com" --queue '+moment().subtract(1, "month").format("YYYY-MM-DD-HH-mm"), // invalid subflag queue date value is past
  'notify --message "Hello world" --open "https://www.holstee.com" --queue '+moment().add(1, "month").format("YY-MM-DD-HH"), // invalid subflag queue date value format
  '--notify --message "Hello world" --open "https://www.holstee.com"', //no action non-flag
  '--notify --message "Hello world" --open "https://www.holstee.com --queue "'+moment().add(1, "month").format("YYYY-MM-DD-HH-mm"), //no action non-flag
]

var shouldNotThrow = [
  'notify --message "Hello world" --open "https://www.holstee.com"',
  'notify --message "Hello world" --open "https://www.holstee.com --queue "'+moment().add(1, "month").format("YYYY-MM-DD-HH-mm"),
  "wallpaper --image https://cdn.shopify.com/s/files/1/0031/5352/t/28/assets/holstee-calendar-2015-03-flow-desktop.jpg?12375621748379006621",
  "wallpaper --image https://cdn.shopify.com/s/files/1/0031/5352/t/28/assets/holstee-calendar-2015-03-flow-desktop.jpg?12375621748379006621 --queue "+moment().add(1, "month").format("YYYY-MM-DD-HH-mm"),
]

describe('Process Text', function(){
  return Promise.map(shouldThrow, function(option){
    it('throw error', function(){
      return main.processText(option).should.throw()
    })    
  })
  return Promise.map(shouldNotThrow, function(option){
    it('throw error', function(){
      return main.processText(option).should.not.throw()
    })    
  })
})

这是我正在尝试测试的非工作*函数的快照。

main.processText = function(text){
  var args = minimist(text.split(" "))
  var actions = _.keys(actionsFlags)
  var flags = _.chain(_.map(actionsFlags, _.keys)).flatten().uniq().value()
  var extraUnparsed = _.extra(actions, args._)
  var providedFlags = _.chain(args).keys().without("_").value()
  var extraParsed = _.extra(flags, providedFlags)
  var validActions = _.intersection(actions, args._)
  var requiredFlags = _.mapObject(actionsFlags, function(flags){
    return _.filterObject(flags, function(flag){
      return flag
    })
  })
  if(extraUnparsed.length) throw new Error("invalid unparsed argument(s): "+extraUnparsed.join(", "))
  if(extraParsed.length) throw new Error("invalid parsed argument(s): "+extraParsed.join(", "))
  if(validActions.length > 1) throw new Error("too many actions: "+validActions.join(", "))
  if(validActions.length == 0) throw new Error("no action: "+actions.join(", "))
  _.each(actions, function(action){
    var missingFlags = _.missing(_.keys(requiredFlags[action]), providedFlags)
    var extraFlags = _.extra(_.keys(requiredFlags[action]), providedFlags)
    if(_.contains(args._, action)){
      if(missingFlags.length) throw new Error(util.format("missing required flags for %s: %s", action, missingFlags.join(", ")))
      if(extraFlags.length) throw new Error(util.format("extra flags for %s: %s", action, extraFlags.join(", ")))
    }
  })
  return text
}

请注意,这不是一个承诺,而且 return 还没有任何承诺。我想要的验证功能之一是检查 url 是否以 200 状态代码响应,这将是一个 request 承诺。如果我更新此函数,那么是否所有函数内容都需要嵌套在 Promise.resolve(false).then() 中?也许 promise 不应该出现在这段代码中,所有 async 验证操作应该存在于其他地方?

我不知道自己在做什么,我有点沮丧。我当然在寻找一些金子弹或任何可以解释这一切的东西。

理想情况下,我可以使用一些帮助来测试这种功能。如果我以后把它变成一个承诺,我仍然希望我的所有测试都能正常工作。


以下是我所说的同步函数和承诺的一些示例代码。

function syncFunction(value){
  if(!value) throw new Error("missing value")
  return value
}

function asyncFunction(url){
  return requestPromise(url)
}

// Both of these will throw errors the same way they will be caught by the promise then you can use `.catch` (in bluebird). 

Promise.resolve(false).then(function(){
  return syncFunction()
})

Promise.resolve(false).then(function(){
  return asyncFunction("http://404.com")
})

我希望这能反映我测试错误的方式,以及 shouldshould not 是否会在我的测试中引发错误。


我忘记了它的承诺,它是一个同步功能,我正在这样测试。

describe('Process Text', function(){
  _.each(shouldThrow, function(option){
    it('throw error ('+option+')', function(){
      expect(function(){
        main.textValidation(option)
      }).to.throw()
    })
  })
  _.each(shouldNotThrow, function(option){
    it('not throw error ('+option+')', function(){
      expect(function(){
        main.textValidation(option)
      }).to.not.throw()
    })
  })
})

强烈推荐npm install chai-as-promised

return doSomethingAsync().should.eventually.equal("foo");

https://github.com/domenic/chai-as-promised