0

最好的方法(海事组织):

  test.add("Expect job not to be done.", function(expect){
       expect(job.done).toBe(false);
  })

此问题已更新,如果有人可能需要,我将在此处发布如何完成测试。

下面的原始问题...

我知道这甚至不可能,但我会清楚地解释我为什么以及如何让它工作:

为什么?

我想做一些在缺少某些东西时不会失败的测试,并且我不想测试所有东西是否存在。

例子:

expect('Something', existingClosureVar.notExistingProperty.toString()).toBe('ho ho');

这将引发如下错误:TypeError: Cannot call method 'toString' of undefined。

解决这个问题很容易,但也很痛苦!

if(existingClosureVar && existingClosureVar.notExistingProperty && existingClosureVar.notExistingProperty.toString){
     expect('Something', existingClosureVar.notExistingProperty.toString()).toBe('ho ho');
}

好吧,但如果它不存在,我什至不会注意到失败的测试!也许可能存在一些更详细的解决方法,但会使这段代码越来越大,而我想要的只是一个简单的东西,这应该是尽可能短的东西。

应该如何工作?

expect('Something', 'existingClosureVar.notExistingProperty.toString()').toBe('ho ho');

不知何故,expect 函数应该可以访问闭包的局部变量以使其工作。它将在 try-catch 上下文中运行字符串,如果失败,测试也会失败。

我想要的确切的东西:

var callingFn = function(){
    var a = 99;
    // remember, the eval can't sit here, must be outside
    var evalString = 'console.log(a)'; // should print 99
    expect(this, evalString); // 
}
var expect = function(context, evalString){
        var fn = function(){
                eval(evalString)
        }
        fn.call(context);

}
new callingFn(); // creates a this in the callingFn that is not the window object

如果我提供上下文,它会起作用,但是......

那将要求我使用“这个”。符号来获取变量。由于很多函数是异步的,并且函数的上下文没有维护(可以维护,但需要做更多的工作)(或者我们可以使用闭包变量来保留上下文变量)。

例子:

var callingFn = function(){
    var context = {b: 1};
    var evalString = 'b'; // prints 1
    expect(context, evalString)
}
var expect = function(context, evalString){
        var fn = function(){
                console.log(eval('this.' + evalString))
        }
        fn.call(context);

}
callingFn()​

我发现了一个丑陋的解决方案:

var callingFn = function(){
    // setup the context
    var context = {};
    // give access to the local closure
    context.fn = function(evalString){
        console.log(eval(evalString))
    }
    var a = 99;
    var evalString = 'a'; // should print 99
    expect(context, evalString);
}
var expect = function(context, evalString){
    context.fn(evalString);
}
callingFn()​

可行的解决方案,但仍然过于冗长:

而且我必须在代码前放 4 行,但这里是Click here to open the Fiddle example

    var callingFn = function(expect){
    // give access to the local closure
    expect.fn = function(evalString){
        try      {return [undefined, eval(evalString)];
        }catch(e){return [e, undefined];}
    }


    var a = {hey: 1, b: {c: 99}};
    console.log(expect('a.b.c', 99)); // true
    console.log(expect('a.b.c.d.f', 99)); // return the error
    console.log(expect('a.b.c', 44)); // false
    console.log(expect('a.hey', 1)); // true
}
var expect = function(evalString, target){
    var result = expect.fn(evalString);
    var err = result[0];
    var output = result[1];
    if(err){
        return err.stack;
    }else{
        return output === target;
    }
}
callingFn(expect)​

共享上下文:

小提琴链接

var callingFn = function(expect){
    // give access to the local closure
    var context = {};
    expect.context = context;
    context.a = {hey: 1, b: {c: 99}};
    console.log(expect('a.b.c', 99)); // true
    console.log(expect('a.b.c.d.f', 99)); // return the error
    console.log(expect('a.b.c', 44)); // false
    console.log(expect('a.hey', 1)); // true
}
var expect = function(evalString, target){
    var fn = function(evalString){
        try      {return [undefined, eval('this.' + evalString)];
        }catch(e){return [e, undefined];}
    }
    var result = fn.call(expect.context, evalString);
    var err = result[0];
    var output = result[1];
    if(err){
        return err.stack;
    }else{
        return output === target;
    }
}
callingFn(expect)​

本地变量 + 字符串:

小提琴链接

var callingFn = function(expect){
    // give access to the local closure
    var a = {hey: 1, b: {c: 99}};
    console.log(expect(a, '.b.c', 99)); // true
    console.log(expect(a, '.b.c.d.f', 99)); // return the error
    console.log(expect(a, '.b.c', 44)); // false
    console.log(expect(a, '.hey', 1)); // true
}
var expect = function(object, evalString, target){
    var fn = function(evalString){
        try      {return [undefined, eval('object' + evalString)];
        }catch(e){return [e, undefined];}
    }
    var result = fn(evalString);
    var err = result[0];
    var output = result[1];
    if(err){
        return err.stack;
    }else{
        return output === target;
    }
}
callingFn(expect)​

// 示例适用于谷歌浏览器,它可能不适用于其他浏览器。

4

2 回答 2

1

不,eval'd 代码在全局范围内运行。为什么需要再次使用 eval?

使用匿名函数而不是将代码放在字符串中,所有与范围相关的问题都会消失。

var callingFn = function() {
    var a = 99;
    // remember, the eval can't sit here, must be outside
    expect(this, function() {
        console.log(a);
    });
}
var expect = function(context, callable) {
    try {
        callable.call(context);
    }
    catch(ex) {
        // handle exception
    }
}

演示:http: //jsfiddle.net/ThiefMaster/P5VCR/

于 2012-09-05T21:57:46.770 回答
1

一句话,没有。您根本无法从eval代码访问闭包中的变量。

eval使用或构造函数创建的Function函数始终在全局范围内运行(或在严格模式下,未定义范围内)。没有办法避免这种情况。

此外,您不能从函数外部(或该函数内部的函数)访问函数的范围。这是因为 JavaScript 有静态作用域;范围在声明时确定。一个函数不能改变它的作用域链。

如果你只是想让你的测试在一个属性未定义的情况下失败,最简单的方法可能是:

if (obj && obj.prop) expect('Some Test', obj.prop) ...
else fail('Some Test');

或者使用一个回调,它的调用被包裹在一个try.

expect('Some Test', function() {
    return obj.prop;
}).toBe('some value');

expect可以使用回调的返回值,或者如果您知道所讨论的标识符将始终被定义,则允许您传递一个简单的值。

function expect(name, o) {
    var expectedValue;
    if (typeof o == 'function') {
        try { expectedValue = o(); }
        catch(ex) { /* fail here */ }
    } else expectedValue = o;

    ...
}

所以你可以调用expect任何一种方式:

function callingFn() {
    var localVar = 1, someObj = {};

    expect('Test var', localVar).toBe(1);
    expect('Test obj', function() { return someObj.missingProp.toString(); }).toBe('value');
}
于 2012-09-05T21:59:12.727 回答