2011-12-15 37 views
22

Tôi đang cố gắng để có được tốt hơn tại đơn vị kiểm tra JavaScript của tôi. Tôi có đoạn mã sau:Hãy chọn một cuộc gọi chọn jQuery?

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

runner thử nghiệm của tôi không có #category đầu vào trên trang web, vậy làm thế nào tôi sẽ còn sơ khai/giả ra bộ chọn jQuery đây? Tôi đã xem cả hai tài liệu jasminsinon, nhưng không thể tìm ra cách để chúng hoạt động ở đây, vì phần khai thác của chúng hoạt động trên các đối tượng, trong đó $ thì không.

+2

Tôi đã không sử dụng một trong các thư viện đó, nhưng '$' là một đối tượng trên thực tế (các hàm là các đối tượng trong JavaScript). Ngoài ra, các hàm jQuery làm việc khi không có gì được chọn - '.val()' sẽ chỉ trả về 'undefined'. – pimvdb

+0

Tôi nhận được rằng '.val()' sẽ trả về 'undefined', nhưng làm thế nào tôi có thể stub rằng để kiểm tra' === '' '? – swilliams

Trả lời

33

Vấn đề ở đây là $() là hàm trả về đối tượng có phương thức val(). Vì vậy, bạn phải stub $() để trả về một đối tượng stubbed có phương thức val.

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

Nhưng lỗi chính ở đây là để mã bạn muốn thử gọi hàm $() để tạo phiên bản mới. Tại sao? Thực hành tốt nhất của nó để tạo ra không có trường hợp mới trong lớp của bạn, nhưng để vượt qua chúng vào constructor. Hãy nói rằng bạn có chức năng đó sẽ nhận được một giá trị ra khỏi một đầu vào, tăng gấp đôi nó, và viết nó trở lại khác:

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

Trong trường hợp này, bạn tạo 2 đối tượng mới bằng cách gọi $(). Bây giờ bạn phải cuống $() để trả lại một mô hình và một sơ khai. Sử dụng ví dụ tiếp theo bạn có thể tránh điều này:

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

Trong khi, trong trường hợp đầu tiên bạn phải còn sơ khai $ trở lại vẫn còn sơ khai, trong trường hợp thứ hai, bạn có thể dễ dàng vượt qua một cuống và một điệp viên vào chức năng của bạn.

Vì vậy, các thử nghiệm Sinon cho một thứ hai sẽ trông như thế này:

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

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

doubleIt(el1, el2) 

assert(el2.withArgs(4).calledOnce) 

Vì vậy, khi bạn không có yếu tố dom đây, bạn chỉ có thể kiểm tra logic ứng dụng của bạn mà không cần phải tạo ra các dom giống như trong ứng dụng của bạn.

+2

Tôi đã làm một cái gì đó như thế này. Tôi đã tái cấu trúc tất cả các thao tác DOM của mình thành các hàm riêng biệt và đặt các cuộc gọi đến các hàm đó. Có thêm lợi ích của việc giữ các phương pháp của tôi thêm mảnh dẻ. – swilliams

+1

Cảm ơn bạn đã giải thích chi tiết về vấn đề này. Tôi gặp vấn đề này trong nút js, nhưng tôi gặp lỗi khi cố gắng gán $ $ $ không được xác định. Tôi tưởng tượng điều này là từ chế độ nghiêm ngặt. Bất kỳ ý tưởng làm thế nào để có được xung quanh này? –

+0

Không nên có một toàn cầu ở phía trước của $ trong trường hợp của IIFE? – Jackie

1

Dưới đây là hướng dẫn khá tốt để kiểm tra chế độ xem của bạn nếu bạn đang sử dụng Backbone.js và Jasmin. Cuộn xuống phần Xem.

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

Đúng, sơ khai hoạt động trên các đối tượng. Tôi đoán điểm tạo ra một cái cuống xem như vậy.

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

Chỉ để có thể hiển thị chế độ xem sau này.

this.view.render(); 

Nói cách khác, phụ thêm '#category' div vào DOM của testrunner, do đó $ có thể hành động theo nó. Nếu div '#category' của bạn không có trong this.view, thì bạn có thể chỉ cần tạo một trang test.html trong đó bạn chạy thử nghiệm riêng biệt của mình. Đây là một mô hình phổ biến trong khuôn khổ Javascript MVC mà tôi sử dụng nhiều hơn cho Backbone đó.

Đây là một ví dụ đơn giản cấu trúc ứng dụng 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.) 

Có thiết lập này bạn chỉ có thể bao gồm các div "#category" trong list_test.html của bạn nếu bạn chưa xảy ra để có nó bên trong một của các chế độ xem.

+0

Rất tốt, cảm ơn liên kết ;-) –

9

jQuery sử dụng công cụ chọn css Sizzle dưới mui xe và được tách ra để chỉ có một vài nơi mà nó móc vào. Bạn có thể chặn điều này để tránh bất kỳ tương tác nào với dom.

jQuery.find là yếu tố quan trọng thay đổi để phản hồi với bất kỳ điều gì bạn muốn. Sinon có thể được sử dụng ở đây hoặc tạm thời hoán đổi chức năng.

ví dụ

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

bạn cũng có thể áp dụng một điều kiện bắt cụ thể với một fallback

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

Trong nghiên cứu gần đây của tôi, tôi đã thực hiện một đối tượng giả rằng phản ứng như một nút dom và bọc rằng trong một đối tượng jQuery. Điều này sau đó sẽ trả lời val() một cách chính xác và có tất cả các phương thức jquery hiện tại mà nó mong đợi. Trong trường hợp của tôi, tôi chỉ đơn giản là kéo các giá trị từ một biểu mẫu. Nếu bạn đang thực hiện thao tác thực tế, bạn có thể cần phải thông minh hơn điều này có lẽ tạo ra một nút dom tạm thời với jQuery đại diện cho những gì bạn mong đợi.

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

Đây là câu trả lời hay nhất, thx! @tissak –

+0

Điều này quá chặt chẽ với nội bộ jQuery. Kiểm tra đơn vị sẽ phá vỡ Nếu jQuery thay đổi cách nó sử dụng sizzle hoặc ngừng sử dụng nó. Kiểm tra đơn vị chỉ giả sử phá vỡ nếu có lỗi trong mã đang được kiểm tra. – Phil

Các vấn đề liên quan