【发布时间】:2016-04-08 12:12:46
【问题描述】:
我正在尝试同时构建和测试一个函数。测试是有道理的,我在理论上喜欢它,但归根结底,它总是背后的痛苦。
我有一个函数,它接受一个字符串并在出现问题时抛出错误,如果一切顺利,它将返回原始的 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
}
注意它不是一个承诺,也没有返回任何承诺。我想要的验证功能之一是检查一个网址是否以200 状态代码响应,这将是一个request 承诺。如果我更新此函数,那么是否所有函数内容都需要嵌套在 Promise.resolve(false).then() 中?也许承诺不应该出现在这段代码中,而所有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")
})
我希望这能反映我测试错误的方式,以及 should 或 should 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()
})
})
})
【问题讨论】:
-
你能把这个问题瘦下来吗?变成更易于阅读(逻辑更少)的方式来演示您要问的基本问题?您有多个问题,我正在尝试找出如何通过有效答案解决此问题。目前突出的主要内容是
Promise.each(shouldThrow, function(option){毫无意义,.each函数接受函数迭代器作为第一个参数,而不是字符串数组。如果你只用几行 throw/not throw 来缩小这种方式,并且最小化你的实现逻辑,那么提供帮助会容易得多 -
很好的反馈@JustinMaat!我只是在寻找一种异步循环遍历数组字符串的方法(使用
.map而不是.each,对不起)然后能够指示promise 是否应该为给定的测试抛出错误。 -
理想情况下,无论方法是什么,我都可以向它传递一个可能引发错误的同步函数或一个可能引发错误的承诺,并且用于测试的接口是相同的,因为理想情况下它被包裹在一个承诺已经和它会被抓住。
-
Ideally whatever the method is I'd be able to pass it a sync function that might throw errors or a promise- 传递同步函数或承诺是什么意思?哪个方法,processText?它目前不接受函数或承诺。您应该考虑将其精简为一小段代码,以演示您要询问的内容。 -
基本上,我想说的是 -
If I make it into a promise later on I still want all my tests to work.,承诺只是将回调代码转换为以同步方式读取。您现有的函数没有采用回调参数,因此将其转换为承诺没有任何意义。如果我可以更好地解决问题,我会尝试发布答案或要点。不过我想你快到了。
标签: javascript node.js unit-testing promise bluebird