0

最良の方法(IMO):

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

この質問は更新されており、誰かがそれを必要とする可能性がある場合に、どのようにテストを終了したかをここに投稿します。

以下の元の質問...

私はそれが不可能かもしれないことを知っていますが、私はそれがなぜそしてどのように機能したいかを明確に説明します:

なんで?

何かが足りなくても失敗しないテストをしたいのですが、存在するかどうかにかかわらず、すべてをテストしたくありません。

例:

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

これにより、次のようなエラーがスローされます。TypeError:未定義のメソッド'toString'を呼び出すことはできません。

これを回避するのは非常に簡単ですが、苦痛でもあります。

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()​

私が見つけた醜い解決策の1つ:

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行を配置する必要がありますが、ここをクリックしてFiddleの例を開きます

    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)​

//例はGoogleChrome用であり、他のブラウザでは機能しない可能性があります。

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 に答える