使用Mocked REST API测试环境

时间:2017-01-26 23:08:29

标签: python rest

假设我有一个非常简单的网络应用程序,如果现任总统是民主党人,则呈现为蓝色,如果他们是共和党人,则呈现红色。 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()以及你能推荐我的库?

4 个答案:

答案 0 :(得分: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'});

查看文档:{​​{3}} $ httpBackend

如果你仍然坚持这个想法,那么你需要在Selenium测试中进行嘲笑 试试这个项目:https://docs.angularjs.org/api/ngMock/service/

它与Python DSL一起用于描述REST响应者。 使用turq您的模拟将如下所示:

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

另外,我建议尝试使用存根而不是模拟并使用此Python模块:mock-server https://turq.readthedocs.io/en/latest/ 您需要创建包含相应的预先填充的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基于Tornado,我认为这是在测试中使用的非常重要的解决方案。

我希望,我的回答很有帮助,也很有用。欢迎讨论!我用Selenium进行了大量的项目,大大小小的测试,测试了客户端和服务器端。

答案 1 :(得分:2)

我会使用龙卷风网络框架。

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"调用脚本。

我希望这能给你一个起点和一个好榜样。

答案 2 :(得分:1)

如果您的load_web_app函数使用requests library访问REST API,则使用requests-mock可以方便地伪造该库的功能以进行测试。

答案 3 :(得分:0)

对于那些偶然发现此问题并且不想最终编写代码来创建自己的API的模拟服务器实现的用户,可以使用Mocktastic(这是Windows的可下载桌面应用程序), MacOS和Linux,提供了易于使用的GUI来设置模拟API服务器。