Einen jQuery-Selektor-Aufruf absetzen?

Lesezeit: 5 Minuten

Ich versuche, besser darin zu werden, mein JavaScript zu testen. Ich habe folgenden Code:

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

Mein Testläufer hat das nicht #category Eingabe auf der Seite, also wie würde ich den jQuery-Selektor hier stubn/mocken? Ich habe mir beide angeschaut Jasmin und Sünde Dokumentation, kann aber nicht herausfinden, wie man sie hier zum Laufen bringt, da ihre Stubs auf Objekten operieren, die $ ist nicht.

  • Ich habe keine dieser Bibliotheken verwendet, aber $ ist tatsächlich ein Objekt (Funktionen sind Objekte in JavaScript). Abgesehen davon funktionieren jQuery-Funktionen, wenn nichts ausgewählt ist – .val() werde einfach wiederkommen undefined.

    – pimvdb

    15. Dezember 2011 um 17:17 Uhr


  • ich verstehe das .val() wird zurückkehren undefinedaber wie kann ich das dann stumpfen, um meine zu testen === ''?

    – Swilliams

    15. Dezember 2011 um 17:35 Uhr

Benutzer-Avatar
Andreas Köberle

Das Problem hier ist das $() ist eine Funktion, die ein Objekt mit der Methode zurückgibt val(). Sie müssen also $() stubben, um ein Stub-Objekt mit der Methode val zurückzugeben.

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

Aber der Hauptfehler besteht darin, den Code, den Sie testen möchten, die Funktion $() aufrufen zu lassen, um neue Instanzen zu erstellen. Wieso den? Es empfiehlt sich, keine neuen Instanzen in Ihrer Klasse zu erstellen, sondern sie an den Konstruktor zu übergeben. Angenommen, Sie haben eine Funktion, die einen Wert aus einer Eingabe erhält, ihn verdoppelt und in eine andere zurückschreibt:

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

In diesem Fall erstellen Sie 2 neue Objekte durch Aufruf $(). Jetzt müssen Sie stubsen $() um einen Mock und einen Stub zurückzugeben. Mit dem nächsten Beispiel können Sie dies vermeiden:

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

Während Sie im ersten Fall $ stubn müssen, um einen Stub zurückzugeben, können Sie im zweiten Fall problemlos einen Stub und einen Spion an Ihre Funktion übergeben.

Der Sinon-Test für den zweiten würde also so aussehen:

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

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

doubleIt(el1, el2)

assert(el2.withArgs(4).calledOnce)

Da Sie hier also keine Dom-Elemente haben, können Sie Ihre Anwendungslogik einfach testen, ohne denselben Dom wie in Ihrer App erstellen zu müssen.

  • Am Ende habe ich so etwas gemacht. Ich habe alle meine DOM-Manipulationen in separate Funktionen umgestaltet und die Aufrufe dieser Funktionen gestummt. Hat den zusätzlichen Vorteil, dass meine Methoden besonders schlank bleiben.

    – Swilliams

    15. Dezember 2011 um 21:13 Uhr

  • Vielen Dank für die ausführliche Erläuterung des Problems. Ich habe dieses Problem in node js, aber ich erhalte eine Fehlermeldung, wenn ich versuche, $ zuzuweisen, dass $ nicht definiert ist. Ich kann mir vorstellen, dass dies aus dem strengen Modus stammt. Irgendeine Idee, wie man das umgehen kann?

    – Timothy González

    5. Oktober 2016 um 21:01 Uhr

  • Sollte bei IIFE nicht ein Global vor dem $ stehen?

    – Jackie

    13. April 2017 um 17:23 Uhr

  • @Andreas Köberle, wie stellt man $ wieder her? Ich habe $.restore(); am Ende des Komponententests und erhalten einen Fehler “$.restore is not a function”. Weißt du, warum?

    – Srdjan Dejanović

    30. Mai 2017 um 7:34 Uhr

jQuery verwendet die CSS-Selektor-Engine Sizzle unter der Haube und wurde entkoppelt, sodass es nur an wenigen Stellen einhakt. Sie können dies abfangen, um jegliche Interaktion mit dem Dom zu vermeiden.

jQuery.find Es ist wichtig zu ändern, um mit allem zu reagieren, was Sie wollen. Sinon könnte hier verwendet werden oder die Funktion temporär auslagern.

z.B

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

Sie könnten auch eine bestimmte Catch-Bedingung mit einem Fallback anwenden

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

In meiner jüngsten Arbeit habe ich ein Dummy-Objekt erstellt, das wie ein Dom-Knoten reagiert, und dieses in ein jQuery-Objekt verpackt. Dies wird dann korrekt auf val() antworten und alle jquery-Methoden vorhanden haben, die es erwartet. In meinem Fall ziehe ich einfach Werte aus einem Formular. Wenn Sie tatsächliche Manipulationen vornehmen, müssen Sie möglicherweise klüger sein als dies, indem Sie möglicherweise einen temporären Dom-Knoten mit jQuery erstellen, der das darstellt, was Sie erwartet haben.

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

  • Dies ist zu eng mit jQuery-Interna gekoppelt. Der Komponententest bricht ab, wenn jQuery die Verwendung von Sizzle ändert oder aufhört, es zu verwenden. Unit-Tests gehen nur davon aus, dass sie brechen, wenn der zu testende Code einen Fehler enthält.

    – Phil

    23. Januar 2015 um 20:10 Uhr

Hier ist eine ziemlich gute Anleitung zum Testen Ihrer Ansichten, wenn Sie Backbone.js und Jasmin verwenden. Scrollen Sie nach unten zum Abschnitt Ansicht.

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

Die Stubs arbeiten zwar mit Objekten. Ich denke, der Punkt, einen solchen View Stub zu erstellen.

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

Ist nur um die Ansicht später rendern zu können.

this.view.render();

Mit anderen Worten, fügen Sie das Div ‘#category’ an das DOM des Testrunners an, damit $ darauf reagieren kann. Wenn sich Ihr „#category“-Div nicht in this.view befindet, können Sie wahrscheinlich einfach eine test.html-Seite erstellen, auf der Sie Ihren isolierten Test ausführen. Dies ist ein allgemeines Muster im Javascript-MVC-Framework, das ich eher an dieses Backbone gewöhnt bin.

Hier ist ein einfaches Beispiel für eine JMVC-Anwendungsstruktur:

/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.)

Mit diesem Setup könnten Sie einfach das “#category” div in Ihre list_test.html einfügen, wenn Sie es nicht bereits in einer der Ansichten haben.

1055250cookie-checkEinen jQuery-Selektor-Aufruf absetzen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy