Javascript 存根 jQuery 选择器调用?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8523974/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 06:21:59  来源:igfitidea点击:

Stub out a jQuery selector call?

javascriptjqueryunit-testingjasminesinon

提问by swilliams

I'm trying to get better at unit testing my JavaScript. I have the following code:

我正在尝试更好地对我的 JavaScript 进行单元测试。我有以下代码:

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

My test runner doesn't have the #categoryinput on the page, so how would I stub/mock out the jQuery selector here? I've looked at both the jasminand sinondocumentation, but can't figure out how to get them to work here, since their stubs operate on objects, which $is not.

我的测试运行器#category在页面上没有输入,那么我将如何在这里存根/模拟 jQuery 选择器?我看了两个茉莉兴农文档,但无法弄清楚如何让他们在这里工作,因为他们的存根对象进行操作,这$是没有的。

回答by Andreas K?berle

The problem here is that $()is a function that returns an object with the method val(). So you have to stub $() to return a stubbed object having the method val.

这里的问题$()是一个函数返回一个带有方法的对象val()。因此,您必须存根 $() 以返回具有方法 val 的存根对象。

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

But the main mistake here is to let the code you want to test call the function $() to create new instances. Why? Its best practice to create no new instances in your class, but to pass them into the constructor. Let's say you have function that will get a value out of an input, double it, and write it back to another:

但是这里的主要错误是让您要测试的代码调用函数 $() 来创建新实例。为什么?最好的做法是不在类中创建新实例,而是将它们传递给构造函数。假设您有一个函数可以从输入中获取一个值,将其加倍,然后将其写回另一个:

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

In this case you create 2 new objects by calling $(). Now you have to stub $()to return a mock and a stub. Using the next example you can avoid this:

在这种情况下,您通过调用创建 2 个新对象$()。现在你必须存根$()返回一个模拟和一个存根。使用下一个示例,您可以避免这种情况:

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

While, in the first case you have to stub $ to return a stub, in the second case you can easily pass a stub and a spy into your function.

而在第一种情况下,您必须存根 $ 以返回存根,在第二种情况下,您可以轻松地将存根和间谍传递到您的函数中。

So the sinon test for the second one would look like this:

所以第二个的 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)

So, as you have no dom elements here, you can simply test your application logic with no need to create the same dom as in your app.

因此,由于这里没有 dom 元素,因此您可以简单地测试应用程序逻辑,而无需创建与应用程序中相同的 dom。

回答by tissak

jQuery uses the css selector engine Sizzle under the hood and was decoupled so there's only a few places where it hooks in. You can intercept this to avoid any interaction with the dom.

jQuery 在引擎盖下使用 css 选择器引擎 Sizzle 并进行了解耦,因此它只有少数几个地方可以挂入。您可以拦截它以避免与 dom 发生任何交互。

jQuery.findis the important one to alter to respond with anything you want. Sinon could be used here or temporarily swapping the function out.

jQuery.find是一个重要的改变以响应你想要的任何东西。可以在这里使用诗农,也可以暂时将功能换掉。

eg

例如

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

you could also apply a specific catch condition with a fallback

您还可以应用带有后备的特定捕获条件

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

In my recent work I have made a dummy object that responds like a dom node and wrapped that in a jQuery object. This will then respond to val() correctly and have all of the jquery methods present that it expects. In my case I'm simply pulling values from a form. If you are doing actual manipulation you might need to be more clever than this perhaps creating a temporary dom node with jQuery that represents what you expected.

在我最近的工作中,我制作了一个像 dom 节点一样响应的虚拟对象,并将其包装在一个 jQuery 对象中。然后这将正确响应 val() 并显示它期望的所有 jquery 方法。就我而言,我只是从表单中提取值。如果您正在进行实际操作,您可能需要比这更聪明,也许可以使用 jQuery 创建一个临时 dom 节点来代表您的预期。

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

回答by Benny Johansson

Here is a pretty good guide to testing your views if you are using Backbone.js and Jasmin. Scroll down to the View section.

如果您使用 Backbone.js 和 Jasmin,这里有一个很好的测试视图的指南。向下滚动到“查看”部分。

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

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

True, the stubs operate on objects. I guess the point of creating a view stub like so.

确实,存根对对象进行操作。我想像这样创建视图存根的意义。

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

Is just to be able to later render the view.

只是为了以后能够渲染视图。

this.view.render();

In other words, append the '#category' div to the DOM of the testrunner, so that $ can act upon it. If your '#category' div is not in this.view, then you probably can just create a test.html page in which you run your isolated test. This is a common pattern in the Javascript MVC framework that I'm more used to that Backbone.

换句话说,将 '#category' div 附加到 testrunner 的 DOM,以便 $ 可以对其进行操作。如果您的“#category”div 不在 this.view 中,那么您可能只需创建一个 test.html 页面,在其中运行您的独立测试。这是 Javascript MVC 框架中的常见模式,我更习惯于那个 Backbone。

Here is a simple JMVC application structure example:

下面是一个简单的 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.)

Having this setup you could just include the "#category" div in your list_test.html if you don't already happen to have it inside one of the views.

有了这个设置,你可以在你的 list_test.html 中包含“#category”div,如果你还没有碰巧在其中一个视图中包含它。