2011-12-15 4 views
22

私は自分のJavaScriptを単体テストすることでより良いものにしようとしています。私は、次のコードをしている:私のテストランナーは、ページ上#category入力をjQueryセレクターコールをスタブアウトしますか?

var categoryVal = $('#category').val(); 
if (categoryVal === '') { 
    doSomething(); 
} 

持っていないので、どのように私はここでjQueryのセレクタから/モックスタブでしょうか?私はjasminsinonの両方のドキュメントを見てきましたが、スタブはオブジェクトの上で動作するので、ここでは動作させる方法を理解できません。$はそうではありません。

+2

私はこれらのライブラリを使用していませんが、 '$'は実際にはオブジェクトです(関数はJavaScriptのオブジェクトです)。それ以外に、jQuery関数は何も選択されていないときに機能します。 '.val()'は単に 'undefined'を返します。 – pimvdb

+0

'.val()'は 'undefined'を返しますが、どうすれば' === '' 'をテストすることができますか? – swilliams

答えて

33

$()は、val()というメソッドでオブジェクトを返す関数です。したがって、メソッドvalを持つスタブ付きオブジェクトを返すために$()をスタブする必要があります。

$ = sinon.stub(); 
$.withArgs('#category').returns(sinon.stub({val: function(){}})); 

しかし、ここでの主な誤りは、テストするコードに関数$()を呼び出して新しいインスタンスを作成させることです。どうして?クラスに新しいインスタンスを作成せず、コンストラクタに渡すのがベストプラクティスです。あなたはそれを倍に、入力のうちの値を取得し、戻って別のことを書きます機能を持っているとしましょう。この場合

function doubleIt(){ 
    $('#el2').val(('#el1').val() *2); 
} 

あなたが$()を呼び出すことによって、2つの新しいオブジェクトを作成します。今度はモックとスタブを返すためにスタブして$()する必要があります。あなたはスタブを返すために$をスタブする必要が最初のケースでは、第2のケースでは、あなたが簡単にあなたの関数にスタブとスパイを渡すことができ、一方で

function doubleIt(el1, el2){ 
    el2.val(el1.val() *2); 
} 

:あなたはこれを避けることができ、次の例を使用します。

だから、1秒間sinonテストは、次のようになります。

var el1 = sinon.stub({val: function(){}}); 
    el1.returns(2); 

var el2 = sinon.spy({val: function(){}}, 'val') 

doubleIt(el1, el2) 

assert(el2.withArgs(4).calledOnce) 

をので、あなたはここにはDOM要素を持っていないとして、あなたは、単に同じDOMを作成する必要はありませんし、アプリケーションのロジックをテストすることができますあなたのアプリで。

+2

私はこのようなことをやめました。私はすべてのDOM操作を別々の関数にリファクタリングし、それらの関数への呼び出しをスタブしました。私の方法をさらに斬新に保つという利点があります。 – swilliams

+1

問題の詳細な説明をいただきありがとうございます。私はノードjsでこの問題を抱えていますが、$が定義されていないと$に代入しようとするとエラーが発生します。これは厳格なモードからのものだと思います。どのようにこれを回避するための任意のアイデアですか? –

+0

IIFEの場合は$の前にグローバルがありませんか? – Jackie

1

ここでは、Backbone.jsとJasminを使用している場合、あなたのビューをテストするための非常に良いガイドです。 [表示]セクションまでスクロールします。

http://tinnedfruit.com/2011/04/26/testing-backbone-apps-with-jasmine-sinon-3.html

真、スタブは、オブジェクトを操作します。私はビューのスタブを作成することのような点を推測します。

this.todoViewStub = sinon.stub(window, "TodoView") 
     .returns(this.todoView); 

後でビューをレンダリングすることができます。言い換えれば

this.view.render(); 

、$はそれに基づいて行動できるように、テストランナーのDOMに「#CATEGORY」のdivを追加します。あなたの '#category' divがthis.viewにない場合は、単体テストを実行するtest.htmlページを作成するだけです。これはJavascript MVCフレームワークの共通のパターンで、私はそのバックボーンにもっと慣れています。ここで

は、単純なJMVCアプリケーション構造の例である:

/todo 
    /models 
     todo.js 
    /list 
     /views 
     init.tmpl 
     listItem.tmpl 
     list.css   
     list.js  (Controller) 
     unitTest.js (Tests for your list.) 
     list_test.html (A html for your unit tests to run on.) 

は、この設定を持つあなたはすでに内部でそれを持って発生しない場合は、ちょうどあなたのlist_test.htmlで「#CATEGORY」div要素を含めることができますのビューの。

+0

いいね、リンクありがとう;-) –

9

jQueryはcssセレクターエンジンSizzleをフードの下で使用しており、デカップリングされているため、フックインする場所はわずかです。ドームとのやりとりを避けるためにこれを傍受することができます。

jQuery.findは、必要なもので応答するために重要な変更です。 Sinonはここで使用するか、一時的に機能を交換することができます。

あなたも私は、DOMノードのように反応するダミーのオブジェクトを作り、中にいることをラップしている私の最近の研究では、フォールバックと

existingEngine = jQuery.find 
jQuery.find = function(selector){ 
    if(selector=='blah'}{ return "test"; } 
    return existingEngine.find.apply(existingEngine, arguments) 
} 

を特定キャッチ条件を適用することができ

existingEngine = jQuery.find 
jQuery.find = function(selector){ console.log(selector) } 
$(".test") 
//>> ".test" 
jQuery.find = existingEngine 

例えばjQueryオブジェクト。これはval()に正しく応答し、期待するすべてのjqueryメソッドを持っています。私の場合は、単にフォームから値を取り出すだけです。実際の操作を行っている場合は、これよりも巧妙である必要があるかもしれませんが、おそらく期待したものを表すjQueryを持つ一時的なDOMノードを作成するでしょう。

obj = { 
    value: "blah", 
    type: "text", 
    nodeName: "input", 
} 
$(obj).val(); // "blah" 
+0

これは最高の答えです。 @tissak –

+0

これはjQueryの内部構造と密接に結びついています。ユニットテストは中断しますjQueryが使用する方法を変更したり、使用しなくなった場合単体テストは、テスト中のコードにバグがある場合にのみ中断すると考えられます。 – Phil

関連する問題