mit Jasmines Spyon nach einer privaten Methode

74

Ist es möglich, die Spyon-Methode des Jasmine Unit Testing Frameworks für private Methoden einer Klasse zu verwenden?

Die Dokumentation gibt dieses Beispiel, aber kann dies für eine private Funktion flexibel sein?

describe("Person", function() {
    it("calls the sayHello() function", function() {
        var fakePerson = new Person();
        spyOn(fakePerson, "sayHello");
        fakePerson.helloSomeone("world");
        expect(fakePerson.sayHello).toHaveBeenCalled();
    });
});
user502014
quelle

Antworten:

133

Fügen Sie einfach einen allgemeinen Parameter <alle> zum spyon () Funktion:

 spyOn<any>(fakePerson, 'sayHello');

Es funktioniert perfekt!

Luillyfe
quelle
3
Ich habe diese Lösung ausprobiert und sie funktioniert einwandfrei. Darüber hinaus kann auf ein privates Feld mithilfe der Array-Indexnotation zugegriffen werden, wie bereits erwähnt.
Koji D'infinte
5
Keiner von beiden hat für mich gearbeitet. Sie können nicht über die Array-Notation zugreifen, da spyOn zwei Argumente akzeptiert. Wenn Sie <any> wie gezeigt eingeben, wird auch ein Fehler mit der falschen Anzahl von Argumenten ausgegeben. Dies ist, was für mich funktioniert hat:spyOn(fakePerson as any, 'sayHello');
Russ
2
Ich mache das auch. Gibt es einen besseren Weg, ohne so "generisch" zu sein? Ich habe zum Beispiel versucht, spyOn <fakePerson> (fakePerson, 'sayHello'); Aber dann beschwert sich immer noch über "Sag Hallo". Ist es möglich, so etwas wie spyOn <fakePerson> <fuction> oder so? Für einen etwas besseren Kontext?
L1ghtk3ira
2
Kann jemand auch erklären, warum diese Lösung funktioniert? Was genau macht das Hinzufügen <any>, damit dies funktioniert?
RisingTide
11
@risingTide Beim Hinzufügen wird <any>die Typprüfung gelöscht , sodass keine Fehler bei der Kompilierungszeit von TypeScript (und keine Fehler in Ihrem Editor) auftreten. Der TypeSciprt wird jedoch letztendlich in Javascript kompiliert, wo jede Methode öffentlich ist, sodass die Typescript-Fehler gelöscht werden.
WindUpDurb
13

Angenommen, es sayHello(text: string)ist eine private Methode. Sie können den folgenden Code verwenden:

// Create a spy on it using "any"
spyOn<any>(fakePerson, 'sayHello').and.callThrough();

// To access the private (or protected) method use [ ] operator:
expect(fakeperson['sayHello']).toHaveBeenCalledWith('your-params-to-sayhello');
  • Erstellen Sie einen Spion für private Methoden mit any.
  • Verwenden Sie den []Operator, um auf die private (oder geschützte) Methode zuzugreifen .
A-Sharabiani
quelle
Abgestimmt, weil Sie nicht das Hauptanliegen angesprochen haben, wie Sie eine private Methode ausspionieren. Dieser Code unterscheidet sich nicht wesentlich von dem, was ursprünglich in der Frage angegeben wurde.
JeffryHouser
@ JeffreyHouser Das sayHelloist eine private Methode. und die erste Zeile ist, wie man einen Spion darauf erstellt. Die zweite Zeile ist der Test.
A-Sharabiani
Ich habs; Ihrer Antwort fehlt die Erklärung, wie man eine private Methode ausspioniert - AKA mit dem <any>Tag. Deshalb war ich verwirrt.
JeffryHouser
9
spyOn<any>(fakePerson, 'sayHello');
expect(fakePerson['sayHello']).toHaveBeenCalled();

Durch Hinzufügen <any>zu spyOn entfernen Sie es aus der Typoskript-Typprüfung. Sie müssen auch die Array-Indexnotation verwenden, um auf eine private Methode (sayHello) in der Testerwartung zuzugreifen

omer
quelle
Während dieser Code die Frage möglicherweise beantwortet, würde die Bereitstellung eines zusätzlichen Kontexts darüber, wie und / oder warum das Problem gelöst wird, den langfristigen Wert der Antwort verbessern.
Igor F.
6

Wenn Sie Typescript für Ihre Objekte verwenden, ist die Funktion nicht wirklich privat.
Sie müssen lediglich den vom spyOnAufruf zurückgegebenen Wert speichern und dann seine callsEigenschaft abfragen .

Am Ende sollte dieser Code für Sie gut funktionieren (zumindest für mich):

describe("Person", function() {
    it("calls the sayHello() function", function() {
        var fakePerson = new Person();
        // save the return value:
        var spiedFunction = spyOn<any>(fakePerson, "sayHello");
        fakePerson.helloSomeone("world");
        // query the calls property:
        expect(spiedFunction.calls.any()).toBeFalsy();
    });
});
jurl
quelle
4
Ich erhalte einen Typfehler, wenn ich versuche, eine nicht exportierte (private) Funktion aufzurufen: Error:(33, 56) TS2345:Argument of type '"sayHello"' is not assignable to parameter of type '"sayGoodbye"'.wobei sayGoodbyeeine öffentliche Methode aktiviert Personund sayGoodbyeprivat ist. Ich muss es auf jeden übertragen ("sayHello" wie jeden)
FlavorScape
Ich brauche hier mehr Kontext, es scheint, dass Ihre Aufgabe nicht funktioniert und nicht der Zugriff auf die private Funktion. Aber versuchen Sie, wie folgt darauf zuzugreifen: person["sayHello"]statt person.sayHello(wenn Sie das tun). Dies ist keine bewährte
Methode
Einverstanden mit @FlavorScape. Typescript (mindestens 1.7 und höher) erwartet, dass die ausspionierte Funktion öffentlich ist, und da sayHello nicht vom Typ sayGoodbye (oder einer der anderen öffentlichen Funktionen) ist, wird ein Fehler ausgegeben. Ich konnte dies nur mit dem oben aufgeführten Spion <any> beheben.
Beartums
Es scheint, als hätten sich die Dinge seit meinem letzten Kommentar geändert. spy<any>kann in der Tat die richtige Antwort sein. Danke
jurl
4

In meinem Fall (Typoskript):

jest.spyOn<any, string>(authService, 'isTokenActual')

ODER mit verspottetem Ergebnis:

jest.spyOn<any, string>(authService, 'isTokenActual').mockImplementation(() => {
  return false;
});
user3765649
quelle
2

Kein Grund, warum Sie nicht auf eine private Funktion außerhalb des Kontexts Ihrer Instanz zugreifen können.

Übrigens ist es keine gute Idee, Objekte auszuspionieren, die Sie testen möchten. Wenn Sie testen, ob eine bestimmte Methode in Ihrer Klasse, die Sie testen möchten, aufgerufen wird, sagt dies nichts aus. Nehmen wir an, Sie haben den Test geschrieben und er ist bestanden. Zwei Wochen später überarbeiten Sie einige Dinge in der Funktion und fügen einen Fehler hinzu. Ihr Test ist also immer noch grün, weil Sie die Funktion aufgerufen haben. B.

Spione sind nützlich, wenn Sie mit Dependency Injection arbeiten , bei dem alle externen Abhängigkeiten vom Konstruktor übergeben und nicht in Ihrer Klasse erstellt werden. Nehmen wir also an, Sie haben eine Klasse, die ein dom-Element benötigt. Normalerweise würden Sie einen jquery-Selektor in der Klasse verwenden, um dieses Element abzurufen. Aber wie wollen Sie testen, ob etwas mit diesem Element gemacht wird? Sicher können Sie es zu Ihren Testseiten HTML hinzufügen. Sie können Ihre Klasse aber auch aufrufen, indem Sie das Element im Konstruktor übergeben. Auf diese Weise können Sie mit spy überprüfen, ob Ihre Klasse wie erwartet mit diesem Element interagiert.

Andreas Köberle
quelle
28
Es ist nicht richtig zu sagen, dass es keine gute Idee ist, Objekte auszuspionieren, die Sie testen möchten. Die Verwendung von Spionen beschränkt sich nicht nur darauf, zu überprüfen, ob eine Funktion einfach aufgerufen wurde, und das war's. Sie können Spione verwenden, um zurückgegebene Werte zu überprüfen, Funktionen für Testfälle, Fehler usw. vollständig zu ersetzen. Ich würde empfehlen, die Jasmin-Dokumentation zu lesen, um ein umfassenderes Verständnis zu erhalten.
Tim McClure
2
Es ist keine gute Idee zu spionieren! ?? Sie können nicht mehr falsch sein! Ja, ich stimme Tim zu und du solltest dir das Dokument ansehen. Wer hat das gewählt?!
AltF4_
3
Das ist nicht was ich sagte. Es ist in Ordnung, Spione zu benutzen. Sie sollten das Objekt, das Sie testen möchten, jedoch als Black Box behandeln. Testen Sie nur, was rein und raus geht. Testen Sie keine Interna der Black Box. Was würden Sie tun, wenn Sie die Methoden des zu testenden Objekts ausspionieren? Es ist also völlig in Ordnung, Rückrufe auszuspionieren, die Sie an das Objekt weitergeben.
Andreas Köberle
2

Typoskript wird zu Javascript kompiliert und in Javascript ist jede Methode öffentlich. Sie können also die Array-Indexnotation verwenden, um auf private Methoden oder Dateien zuzugreifen, nämlich:

Object['private_field']
Koji D'infinte
quelle
Der Blog-Link ist leider tot.
geisterfurz007
1
Hier unten abgestimmt, Teil für den toten Blog-Link; Teil für die Tatsache, dass wir keine Objektarray-Notation verwenden können, um einen Spion einzurichten.
JeffryHouser
0

Wenn Sie private Funktionen innerhalb einer Klasse testen möchten, fügen Sie Ihrer Klasse einen Konstruktor hinzu, der signalisiert, dass diese privaten Funktionen zurückgegeben werden.

Lesen Sie dies durch, um zu sehen, was ich meine: http://iainjmitchell.com/blog/?p=255

Ich habe eine ähnliche Idee verwendet und bis jetzt funktioniert es großartig!

StevenMcD
quelle
5
Wenn Sie Ihre private Methode veröffentlichen, ist sie nicht mehr privat. Übrigens, wie ich in meiner Antwort beschrieben habe, ist es nicht sehr sinnvoll, die private Methode zu testen.
Andreas Köberle
1
Wir können uns darauf einigen, dass wir uns nicht einig sind. Unsere Javascript-Codebasis ist riesig und wir stellen nur einige unserer Klassen mit öffentlichen Funktionen / Eigenschaften zur Verfügung. In diesen privaten Funktionen wird viel Logik gehandhabt. Ich veröffentliche nur eine private Methode, damit das Testframework darauf zugreifen kann. Wenn der Konstruktor nicht korrekt aufgerufen wird, wird die private Funktion nicht zurückgegeben.
StevenMcD
0
const spy = spyOn<any>(component, 'privateMethod');
expect(spy).toHaveBeenCalled();

Erstellen Sie eine lokale Konstante des Spionageobjekts, um Flusenwarnungen bezüglich des Objektzugriffs über Zeichenfolgenliterale zu vermeiden.

OOP
quelle