2017-01-26 79 views
9

假設我有一個非常簡單的網絡應用程序,如果現任總統是民主黨人,則呈現藍色,如果他們是共和黨人,則呈現紅色。一個REST API來獲取當前的總裁,通過端點:使用Mocked REST API的測試環境

/presidents/current 

目前返回的JSON對象:

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

所以,當我的頁面加載我所說的終點,我顯示紅色或藍色取決於誰返回。

我想測試這個HTML/JavaScript頁面,我希望嘲笑後端,以便我可以從測試環境中控制API響應。例如:

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

所以,問題是我應該如何實現功能configure_endpoint()什麼庫,你可以推薦嗎?

回答

1

如果您的load_web_app函數使用requests library來訪問REST API,使用requests-mock是僞造該庫的功能以進行測試的便捷方法。

+0

沒有load_web_app()只使用硒在瀏覽器中加載html/js文件。我需要通過創建一個web應用連接到的api服務器來模擬後端。這個模擬服務器應該可以在測試環境中進行配置。 – Baz

+0

您是否有充分的理由希望隔離被測試的系統,這與被測試的客戶端業務邏輯相距甚遠,而不是更接近它? (我假設你使用了一些已經經過良好測試的網絡訪問庫,而不是自己編碼的,如果你這樣做,你的測試當然也必須覆蓋那部分。) –

+0

這個API是已經覆蓋了測試。這些測試涉及調用api並測試是否收到正確的響應。我想現在測試Web應用程序中的流程,並測試該應用程序對於我希望支持的瀏覽器的預期行爲。換句話說,我想將前端視爲一個子系統,併爲其編寫子系統測試。 – Baz

2

我會使用龍捲風web框架。

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

這是我用來嘲弄一個REST API,它是一個獨立的腳本代碼,但它可以嵌入到你的測試環境中也是如此。

我定義了一個JSON文件,它定義了不同的路徑組件以及應該返回的內容。就像這樣:

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

我救這一個mock.json並稱爲腳本參數mock_rest.py --data-file="./mock.json"

我希望能給你一個出發點和一個很好的例子。

4

正如@Kie提到的,configure_endpoint實現是不夠的,如果你打算在Selenium Python代碼中存儲整個服務器端的話。您需要一個Web服務器或任何將通過HTTP響應來自測試環境內的請求的內容。

看起來問題部分是關於客戶端代碼的測試。我所看到的是你試圖對客戶端邏輯進行單元測試,但是使用集成測試套件來檢查這個邏輯(這很奇怪)。

主要思路如下。

您正試圖測試客戶端代碼。所以,讓我們讓客戶端模擬!因爲這部分代碼完全是客戶端相關的東西。

如果你真的想要模擬,而不是存根(看這裏的區別:https://stackoverflow.com/a/3459491/882187)這是一個更好的方法來模擬你的Javascript代碼中的HTTP請求。僅僅因爲你正在測試客戶端代碼,而不是服務器端邏輯的某些部分。如果將它與服務器端分離開來 - 是一個好主意,當你的項目變得越來越多時,你會愛上它,而越來越多的端點將會出現。

例如,你可以用下面的辦法:

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

實際上,這實現依賴於你所擁有的客戶端爲框架做。如果jQuery,那麼我的例子就夠了,但看起來很羅嗦。如果你有一些更先進的,像AngularJS,你可以做同樣的2-3行代碼:

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

退房文檔:https://docs.angularjs.org/api/ngMock/service/ $ httpBackend

如果你仍然堅持這個想法,你需要模擬在Selenium測試中,請 試試這個項目:https://turq.readthedocs.io/en/latest/

它與Python DSL一起用於描述REST響應者。 使用turq你的嘲弄將如下所示:

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

另外,我建議嘗試存根,而不是嘲笑和使用Python模塊:mock-serverhttps://pypi.python.org/pypi/mock-server/0.3.7 您需要創建一個包含相應的預填充的目錄佈局JSON響應並添加一些樣板代碼以使mock-server在「localhost:8080」上響應。您的示例中的目錄佈局看起來就像這樣:

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

mock_server基於龍捲風,它是使用在測試中,我覺得非常沉重的解決方案。

我希望,我的回答是有用的和內容豐富的。歡迎來洽談!我使用Selenium進行了大量的項目,大大小小的測試,測試了客戶端和服務器端。