Вырезать вызов селектора jQuery?
Я пытаюсь улучшить модульное тестирование своего JavaScript. У меня есть следующий код:
var categoryVal = $('#category').val();
if (categoryVal === '') {
doSomething();
}
Мой тестовый бегун не имеет ввода #category
на странице, так как я мог бы заглушить/издеваться над селектором jQuery? Я посмотрел на jasmin и sinon, но не может понять, как заставить их работать здесь, поскольку их заглушки работают с объектами, а $
- нет.
Ответы
Ответ 1
Проблема заключается в том, что $()
- это функция, которая возвращает объект с помощью метода val()
. Таким образом, вы должны заглушить $(), чтобы вернуть заштрихованный объект, имеющий метод val.
$ = sinon.stub();
$.withArgs('#category').returns(sinon.stub({val: function(){}}));
Но главная ошибка здесь - позволить коду, который вы хотите протестировать, вызвать функцию $() для создания новых экземпляров. Зачем? Лучше всего не создавать новые экземпляры в вашем классе, а передавать их в конструктор. Скажем, у вас есть функция, которая будет получать значение из ввода, удвоить его и записать обратно:
function doubleIt(){
$('#el2').val(('#el1').val() *2);
}
В этом случае вы создаете 2 новых объекта, вызывая $()
. Теперь вам нужно заглушить $()
, чтобы вернуть макет и заглушку. Используя следующий пример, вы можете избежать этого:
function doubleIt(el1, el2){
el2.val(el1.val() *2);
}
В то время как в первом случае вам нужно заглушить $, чтобы вернуть заглушку, во втором случае вы можете легко передать заглушку и шпион в вашу функцию.
Итак, синус-тест для второго будет выглядеть так:
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, вы можете просто протестировать свою логику приложения, не создавая тем же самым доменом, что и в своем приложении.
Ответ 2
jQuery использует движок селектора css Sizzle под капотом и отключен, поэтому есть только несколько мест, где он зацепляется. Вы можете перехватить это, чтобы избежать любого взаимодействия с dom.
jQuery.find - важный, который нужно изменить, чтобы реагировать на все, что угодно. Sinon можно использовать здесь или временно отключить функцию.
например,
existingEngine = jQuery.find
jQuery.find = function(selector){ console.log(selector) }
$(".test")
//>> ".test"
jQuery.find = existingEngine
вы также можете применить определенное условие catch с резервным
existingEngine = jQuery.find
jQuery.find = function(selector){
if(selector=='blah'}{ return "test"; }
return existingEngine.find.apply(existingEngine, arguments)
}
В моей недавней работе я сделал фиктивный объект, который отвечает как dom node и завернут в объект jQuery. Затем он будет корректно отвечать на val() и будет иметь все имеющиеся jquery-методы, которые он ожидает. В моем случае я просто вытягиваю значения из формы. Если вы делаете фактические манипуляции, вам может понадобиться быть более умным, чем это, возможно, создавая временный dom node с jQuery, который представляет то, что вы ожидали.
obj = {
value: "blah",
type: "text",
nodeName: "input",
}
$(obj).val(); // "blah"
Ответ 3
Вот довольно хорошее руководство по тестированию ваших просмотров, если вы используете 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();
Другими словами, добавьте '# category' div в DOM testrunner, чтобы $мог действовать на него. Если ваш '# category' div не находится в this.view, то вы, вероятно, можете просто создать страницу test.html, в которой вы запускаете изолированный тест. Это общий шаблон в структуре JavaScript MVC, который я больше использую для этой Backbone.
Вот простой пример структуры приложения 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.)
С помощью этой настройки вы можете просто включить div "#category" в свой list_test.html, если вы еще не обнаружили его внутри одного из представлений.