2017-01-26 2 views
9

Lets sagen, ich habe eine sehr einfache Web-App, die als blau dargestellt wird, wenn der aktuelle Präsident ein Demokrat und rot ist, wenn sie ein Republikaner sind. Ein REST-API verwendet, um den aktuellen Präsidenten zu bekommen, über den Endpunkt:Testumgebung mit Mocked REST API

/presidents/current 

die derzeit die Json-Objekt zurückgibt:

{name: "Donald Trump", party: "Republican"} 

Also, wenn meine Seite geladen ich den Endpunkt anrufen, und ich zeige rot oder blau, je nachdem, wer zurückgegeben wird.

Ich möchte diese HTML/Javascript-Seite testen und ich möchte das Back-End verspotten, so dass ich aus der Testumgebung die API-Antworten steuern kann. Zum Beispiel:

def test_republican(): 
    # configure the response for this test that the web app will receive when it connects to this endpoint 
    configure_endpoint(
     "/presidents/current", 
     jsonify(
      name="Donald Trump", 
      party="Republican" 
     ) 
    ) 

    # start the web app in the browser using selenium 
    load_web_app(driver, "http://localhost:8080") 

    e = driver.find_element_by_name("background") 
    assert(e.getCssValue("background-color") == "red") 


def test_democrat(): 
    # configure the response for this test that the web app will receive when it connects to this endpoint 
    configure_endpoint(
     "/presidents/current", 
     jsonify(
      name="Barack Obama", 
      party="Democrat" 
     ) 
    )  

    # start the web app in the browser using selenium 
    load_web_app(driver, "http://localhost:8080") 

    e = driver.find_element_by_name("background") 
    assert(e.getCssValue("background-color") == "blue") 

Die Frage ist also, wie soll ich die Funktion configure_endpoint() implementieren und welche Bibliotheken können Sie mir empfehlen?

Antwort

1

Wenn Ihre load_web_app-Funktion die requests library für den Zugriff auf die REST-API verwendet, ist die Verwendung von eine bequeme Möglichkeit, die Funktionalität dieser Bibliothek zu Testzwecken zu fälschen.

+0

Kein load_web_app() lädt einfach die HTML/Js-Datei im Browser mit Selenium. Ich muss das Backend verspotten, indem ich einen API-Server erstelle, mit dem sich die Web-App verbindet. Dieser Mock-Server sollte innerhalb der Testumgebung konfigurierbar sein. – Baz

+0

Haben Sie einen guten Grund, das zu testende System weit entfernt von der getesteten Client-Geschäftslogik zu isolieren, anstatt näher dran zu sein? (Ich gehe davon aus, dass Sie eine bereits gut getestete Bibliothek für den Netzwerkzugriff verwenden und dies nicht selbst programmieren. Falls ja, müssten Ihre Tests natürlich auch diesen Teil abdecken.) –

+0

Die API ist bereits mit Tests abgedeckt. Diese Tests umfassen das Aufrufen der API und das Testen, dass die richtige Antwort empfangen wurde. Ich möchte nun den Fluss innerhalb der Web-App testen und testen, ob sich die App für die Browser, die ich unterstützen möchte, wie erwartet verhält. Mit anderen Worten, ich möchte das Frontend als Subsystem behandeln und Subsystemtests dafür schreiben. – Baz

2

Ich würde Tornado-Web-Framework verwenden.

import json 
import functools 
import operator 
from tornado import ioloop, web, gen 
from tornado.options import define, options 

define("data_file", default='default/mock.json', type=str) 

class Handler(web.RequestHandler): 

    def data_received(self, chunk): 
     pass 

    def initialize(self, data): 
     self.data = data 

    @gen.coroutine 
    def get(self, *args, **kwargs): 
     path = self.request.path.split("/")[1:] 
     path = functools.reduce(
      operator.add, 
      [[k, v[0].decode("utf-8")] for k, v in   self.request.query_arguments.items()], 
      path 
     ) 

     try: 
      self.write(functools.reduce(operator.getitem, path, self.data)) 
     except KeyError: 
      self.set_status(404) 


class Application(web.Application): 
    def __init__(self): 
     data = {} 
     with open(options.data_file) as data_file: 
      data = json.load(data_file) 

     handlers = [ 
      ('(.*)', Handler, {"data": data}) 
     ] 
     settings = dict(
      gzip=True, 
      static_hash_cache=True, 
     ) 
     web.Application.__init__(self, handlers, **settings) 


    def main(): 
     io_loop = ioloop.IOLoop.instance() 
     backend_application = Application() 
     backend_application.listen(8001) 
     io_loop.start() 

    if __name__ == "__main__": 
     main() 

Dies ist ein Code, den ich für spöttische ein REST-API verwendet, die ein eigenständiges Skript, aber es kann auch in der Testumgebung eingebettet werden.

Ich definierte eine JSON-Datei, die die verschiedenen Pfadkomponenten definiert und was zurückgegeben werden soll. Wie folgt aus:

{ 
    "presidents": { 
     "current": { 
      "name": "Donald Trump", 
      "party": "Republican" 
     } 
    } 
} 

gespeichert Ich dies zu einem mock.json und nannte das Skript mit einem Parameter mock_rest.py --data-file="./mock.json".

Ich hoffe, dass Sie einen Ausgangspunkt und ein gutes Beispiel gibt.

4

Wie @Kie erwähnt, configure_endpoint Implementierung wird nicht genug sein, wenn Sie die gesamte Server-Seite in Selenium Python-Code stubeln werden. Sie benötigen einen Webserver oder was auch immer, der über HTTP Anfragen aus der Testumgebung beantwortet.

Es sieht so aus, als ob die Frage teilweise das Testen von clientseitigem Code betrifft. Was ich sehe, ist, dass Sie versuchen, unit-Test für clientseitige Logik zu machen, aber Integrationstestsuite verwenden, um diese Logik zu überprüfen (es ist seltsam).

Die Hauptidee ist wie folgt.

Sie versuchen, clientseitigen Code zu testen. Also, machen wir Mocks clientseitig! Weil dieser Teil des Codes vollständig clientseitig ist.

Wenn Sie tatsächlich Mocks haben wollen, keine Stubs (beachten Sie den Unterschied hier: https://stackoverflow.com/a/3459491/882187) ist es eine bessere Möglichkeit, HTTP-Anfragen in Ihrem Javascript-Code zu verspotten. Nur weil Sie einen clientseitigen Code testen, nicht einige Teile der serverseitigen Logik.

Es von jeder Server-Seite isoliert ist - ist eine großartige Idee, die Sie lieben würden, wenn Ihr Projekt wachsen wird, während mehr und mehr Endpunkte erscheinen werden.

Zum Beispiel können Sie die folgende Methode verwenden:

var restResponder = function() { // the original responder your client-side app will use 
    this.getCurrentPresident = function(successCallback) { 
    $.get('/presidents/current', callback); 
    } 
}; 

var createMockResponder = function(president, party){ // factory that creates mocks 
    var myPresident = president; 
    var myParty = party; 

    return function() { 
    this.getCurrentPresident = function (successCallback) { 
     successCallback({"name": myPresident, "party": myParty}); 
    } 
    }; 
} 

// somewhere swap the original restResponder with new mockResponder created by 'createMockResponder' 

// then use it in your app: 

function drawColor(restResponder, backgroundEl) { 
    restResponder.getCurrentPresident(function(data){ 
    if (data.party == "Democrat") $(backgroundEl).style('background-color', 'blue') 
    else if (data.party == "Republican") $(backgroundEl).style('background-color', 'red') 
    else console.info('Some strange response from server... Nevermind...'); 
    }); 
} 

Praktisch diese Implementierung hängt davon ab, was Sie haben auf der Client-Seite als Rahmen. Wenn jQuery, dann ist mein Beispiel genug, aber es sieht sehr wortreich aus. Falls Sie etwas weiter fortgeschritten ist, wie AngularJS haben, können Sie das gleiche in 2-3 Zeilen Code tun:

// Set up the mock http service responses 
$httpBackend = $injector.get('$httpBackend'); 
// backend definition common for all tests 
authRequestHandler = $httpBackend.when('GET', '/auth.py') 
           .respond({userId: 'userX'}, {'A-Token': 'xxx'}); 

Überprüfen Sie die Dokumentation aus: https://docs.angularjs.org/api/ngMock/service/ $ httpBackend

Wenn Sie immer noch halten Sie sich an die Idee, dass Sie Mocks innerhalb Selenium Tests benötigen, bitte versuchen Sie dieses Projekt: https://turq.readthedocs.io/en/latest/

Es dient mit Python DSL zur Beschreibung von REST-Respondern. Mit turq Ihre Mocks werden wie folgt aussehen:

path('/presidents/current').json({'name':'Barack Obama', 'party': 'Democrat'}, jsonp=False) 

Auch würde ich empfehlen, Stubs statt Mocks zu versuchen, diesen Python-Modul verwenden: mock-serverhttps://pypi.python.org/pypi/mock-server/0.3.7 Sie erforderlich sind, um das Verzeichnis-Layout entsprechenden Pre-besiedelten erstellen JSON-Antworten und Hinzufügen eines Boilerplate-Codes, um die mock-server Antwort auf 'localhost: 8080' zu machen. Die Verzeichnisstruktur für Ihr Beispiel wie folgt aussehen:

stub_obama/ 
    presidents/ 
    current/ 
     GET_200.json  # will contain {"name": "Barack Obama", "party": "Democrat"} 
stub_trump/ 
    presidents/ 
    current/ 
     GET_200.json  # will contain {"name": "Donald Trump", "party": "Republican"} 

Aber die mock_server auf Tornado basiert, ist es sehr schwer Lösung für in Tests mit, denke ich.

Ich hoffe, meine Antwort ist hilfreich und informativ. Willkommen, um es zu diskutieren! Ich habe jede Menge Projekte mit Selenium gemacht, große und kleine Tests, getestet auf Client- und Server-Seite.