2017-01-26 20 views
9

Powiedzmy, że mam bardzo prostą aplikację internetową, która jest prezentowana jako niebieska, jeśli obecny prezydent jest demokratą, a czerwony, jeśli jest republikaninem. REST API służy do uzyskania obecnego prezydenta, poprzez punkt końcowy:Środowisko testowe z wyśmianym REST API

/presidents/current 

który obecnie zwraca obiekt json:

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

Więc kiedy moi strona ładuje się nazywam końcowego i pokażę czerwony lub niebieski w zależności od tego, kto jest zwracany.

Chcę przetestować tę stronę HTML/javascript i chcę wyśmiać back-end, dzięki czemu mogę kontrolować z poziomu środowiska testowego odpowiedzi API. Na przykład:

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") 

Więc pytanie brzmi, w jaki sposób należy realizować funkcję configure_endpoint() i jakie biblioteki możesz mi polecić?

Odpowiedz

1

Jeśli twoja funkcja load_web_app korzysta z requests library w celu uzyskania dostępu do REST API, użycie requests-mock jest wygodnym sposobem na sfałszowanie tej biblioteki do celów testowych.

+0

No load_web_app() po prostu ładuje plik html/js w przeglądarce przy użyciu selenu. Muszę wyśmiać backend, tworząc serwer api, z którym łączy się aplikacja internetowa. Ten fałszywy serwer powinien być konfigurowalny z poziomu środowiska testowego. – Baz

+0

Czy masz dobry powód, aby chcieć wyizolować testowany system z dala od testowanej logiki biznesowej klienta, a nie bliżej niego? (Zakładam, że używasz już dobrze przetestowanej biblioteki do dostępu do sieci i nie kodujesz tego samemu. Jeśli to zrobisz, twoje testy będą oczywiście również musiały pokryć tę część również.) –

+0

Interfejs API to już pokryte testami. Testy te wymagają wywołania api i sprawdzenia, czy otrzymano poprawną odpowiedź. Chciałbym teraz przetestować przepływ w aplikacji internetowej i przetestować, czy aplikacja zachowuje się zgodnie z oczekiwaniami dla tych przeglądarek, które chcę obsługiwać. Innymi słowy, chciałbym traktować front-end jako podsystem i napisać dla niego testy sub-systemu. – Baz

2

Chciałbym użyć ramy sieci tornado.

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() 

Jest to kod użyłem dla wyśmianie REST-API, które jest samodzielny skrypt, ale może być osadzony w środowisku testowym, jak również.

Zdefiniowałem plik JSON, który definiuje różne składniki ścieżki i co powinien zostać zwrócony. Tak:

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

Uratowałem to do mock.json i nazywa skrypt z parametrem mock_rest.py --data-file="./mock.json".

Mam nadzieję, że da ci to punkt wyjścia i dobry przykład.

4

Jak wspomniałem @Kie, implementacja configure_endpoint nie wystarczy, jeśli zamierzasz odgalić całą stronę serwera w kodzie selenu Python. Potrzebny byłby serwer WWW lub cokolwiek, co będzie odpowiadać przez HTTP na żądania z poziomu środowiska testowego.

Wygląda na to, że pytanie dotyczy częściowo testowania kodu po stronie klienta. Widzę, że próbujesz wykonać test jednostkowy dla logiki po stronie klienta, ale użyj zestawu testów integracyjnych, aby sprawdzić tę logikę (to dziwne).

Główna idea jest następująca.

Próbujesz przetestować kod po stronie klienta. Więc róbmy też mocky po stronie klienta! Ponieważ ta część kodu jest całkowicie powiązana z klientem.

Jeśli rzeczywiście chcesz mieć mocks, nie stubs (patrz różnica tutaj: https://stackoverflow.com/a/3459491/882187) jest to lepszy sposób na wyłudzanie żądań HTTP w swoim kodzie JavaScript. Tylko dlatego, że testujesz fragment kodu po stronie klienta, a nie niektóre elementy logiki po stronie serwera.

Izolacja od strony serwera - to świetny pomysł, który pokochasz, gdy projekt się rozwinie, a pojawi się coraz więcej punktów końcowych.

Na przykład, można użyć następujące podejście:

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...'); 
    }); 
} 

Praktycznie ta implementacja zależy co masz na stronie klienta jako ramy. Jeśli jQuery, to mój przykład jest wystarczający, ale wygląda na bardzo rozwlekły. W przypadku gdy masz coś bardziej zaawansowanego, jak AngularJS, można zrobić to samo w 2-3 linii kodu:

// 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'}); 

Wyjazd docs: https://docs.angularjs.org/api/ngMock/service/ $ httpBackend

Jeśli nadal trzymać się pomysł, że potrzebujesz makiety wewnątrz testów Selenium, proszę wypróbuj ten projekt: https://turq.readthedocs.io/en/latest/

Służy on z python DSL do opisywania respondentów REST. Korzystanie turq twoi mocks będzie wyglądać następująco:

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

Również polecam spróbować odcinki zamiast mocks i wykorzystać ten moduł Pythona: mock-serverhttps://pypi.python.org/pypi/mock-server/0.3.7 które są wymagane, aby utworzyć strukturę katalogów zawierających odpowiadające wstępnie wypełnione Odpowiedzi JSON i dodanie kodu standardowego w celu odpowiedzi na "localhost: 8080". Układ katalogów na swoim przykładzie będzie wyglądać następująco:

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"} 

Ale mock_server opiera się na Tornado, to jest bardzo ciężkie rozwiązanie do stosowania w testach myślę.

Mam nadzieję, że moja odpowiedź jest pomocna i pouczająca. Zapraszamy do dyskusji! Zrobiłem mnóstwo projektów z Selenium, duże i małe testy, testowane po stronie klienta i po stronie serwera.