2016-05-03 7 views
1

Ich habe folgende Funktionen:Testing JavaScript Callback-Funktionen mit Jasmin

function getPersonData(id) { 
    retrieveData(
    id, 
    function(person) { 
     if(person.name) { 
     displayPerson(person); 
     } 
    } 
} 

function retrieveData(id, successCallBack) { 
    executeRequest(id, { 
     success: successCallBack 
    }); 
} 

getPersonData ruft eine Information einer Person auf der ID basiert. Es wiederum ruft retrieveData auf, indem es die ID und eine Funktion successCallBack übergibt.

retrieveData nimmt die ID und successCallBack und ruft eine andere Funktion, executeRequest, die die Daten erhält und ein Objekt Person zurückgibt.

Ich versuche getPersonData und haben folgende spec

einrichten zu testen
describe("when getPersonData is called with the right person id", function() { 
    beforeEach(function() { 
    spyOn(projA.data, "retrieveData").and.returnValue(
     { 
     'name': 'john' 
     } 
    ); 
    spyOn(projA.data, "displayPerson"); 
    projA.data.getPersonData("123"); 
    }); 

    it("displays the person's details", function() { 
    expect(projA.data.displayPerson).toHaveBeenCalled(); 
); 
}); 

Aber wenn die Spezifikation der displayPerson Methode aufgerufen wird, wird nicht ausgeführt. Dies liegt daran, dass die Personendaten, die von dem Erfolgsrückruf function(person) zurückgegeben werden, nicht übergeben werden, obwohl ich retrieveData verspottet habe, um ein Ergebnis zurückzugeben.

Meine Frage ist: Ist das der richtige Weg, CallBack-Funktionen zu testen? Wie auch immer, was mache ich falsch?

+0

Ich denke, die Verwendung von 'returnValue' für eine Funktion, die keinen Wert zurückgibt, würde nicht funktionieren. Gibt es Alternativen? – user12222

+0

Ich habe mich entschieden, 'Funktion (Person) { zu verschieben, wenn (person.name) { displayPerson (Person); } } 'zu einer neuen Methode, um beim Testen zu helfen. Aber ich würde trotzdem gerne wissen, wie der obige Code getestet werden könnte. – user12222

Antwort

2

Ok, so ist Jasmin in vielen subtilen Weise heikel und ich denke, es gibt zwei Hauptprobleme mit Ihrem Code

  1. Sie haben viel zu viele asynchronen Aufrufe ineinander geschlungen. Was an sich kein Problem ist, aber es macht das Testen in JASMINE um einiges schwieriger. Zum Beispiel, was ist der Punkt, eine retrieveData Funktion zu haben, die nur executeRequest Funktion mit den exakt gleichen Parametern, aber auf eine etwas andere Weise anruft.

ich Ihre getPersonData neu geschrieben, wie dieser in Jasmin

function getPersonData(id) { 
    // this is needed to properly spy in Jasmine 
    var self = this; 

    //replaced retrieveData with just execute request 
    // self is required to properly spy in Jasmine 
    self.executeRequest(id, { 
    success: function(person) { 
    if (person.name) { 
     self.displayPerson(person); 
    } 
    } 
    }) 
} 

//I don't know what exactly executeRequest does 
//but I took the liberty to just make it up for this example 
function executeRequest(id, callback) { 
callback.success({ 
    name: id 
    }); 
} 

//I also assumed that projA would look something like this 
var projA = { 
data: { 
    getPersonData: getPersonData, 
    retrieveData: retrieveData, 
    displayPerson: displayPerson, 
    executeRequest: executeRequest 
} 
}; 

2. Um zu asynchronen Code zu testen, müssen Sie mit dem Test einen done Rückruf aufzunehmen. Wenn Sie außerdem erwarten, dass eine Callback-Funktion automatisch ausgelöst wird, müssen Sie sie innerhalb einer setTimeout-Funktion einrichten, andernfalls wird sie nie ausgelöst. Hier ist ein angepasstes Beispiel:

describe("when getPersonData is called with the right person id", function() { 
    beforeEach(function() { 
     //you should not spyOn retriveData or executeCode because it will override the function you wrote and will never call displayPerson 

     // you should only have spies on the methods you are testing otherwise they will override other methods 
     spyOn(projA.data, "displayPerson"); 
    }); 

    it("displays the person's details", function(done) { 
     // it's better to call the function within specific test blocks so you have control over the test 
     projA.data.getPersonData("123"); 

     // at this point, it will just have called the getPersonData but will not call executeRequest 
     setTimeout(function() { 
     //this block will just call executeRequest 
     setTimeout(function() { 
     //this block will finally call displayPerson 
     expect(projA.data.displayPerson).toHaveBeenCalled(); 

     //tell jasmine the test is done after this 
     done(); 
     }) 
     }) 
    }); 
    }) 
Verwandte Themen