使用模拟restapi的测试环境

2024-05-16 10:58:47 发布

您现在位置:Python中文网/ 问答频道 /正文

假设我有一个非常简单的web应用程序,如果现任总统是民主党人,则显示为蓝色;如果现任总统是共和党人,则显示为红色。REST API用于通过端点获取当前总裁:

/presidents/current

当前返回json对象:

^{pr2}$

因此,当我的页面加载时,我调用端点,并根据返回者显示红色或蓝色。在

我希望测试这个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(),你能推荐什么库?在


Tags: thenametestwebappconfiguredrivercurrent
3条回答

如果您的load_web_app函数使用^{} library来访问restapi,那么使用^{}是一种方便的方法,可以伪造该库的功能以进行测试。在

我会使用tornado 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的代码,REST-API是一个独立的脚本,但它也可以嵌入到测试环境中。在

我定义了一个JSON文件,它定义了不同的路径组件和应该返回的内容。像这样:

^{pr2}$

我把这个保存到mock.json文件并使用参数mock_rest.py --data-file="./mock.json"调用脚本。在

我希望这能给你一个起点和一个很好的例子。在

正如@Kie所提到的,configure_endpoint实现是不够的,如果您要在Selenium Python代码中存根整个服务器端的话。您需要一个web服务器或任何通过HTTP响应测试环境中的请求的服务器。在

看来问题的一部分是关于客户端代码的测试。我看到的是,您试图对客户端逻辑进行单元测试,但是使用集成测试套件来检查这个逻辑(这很奇怪)。在

主要思路如下。在

您正在尝试测试客户端代码。所以,让我们也在客户端进行mocks!因为这部分代码完全与客户端相关。在

如果您真的想要mock,而不是stub(注意这里的区别: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行代码来做同样的事情:

^{pr2}$

查看文档:https://docs.angularjs.org/api/ngMock/service/$httpBackend

如果您仍然坚持这个想法,请在Selenium测试中使用mocks 试试这个项目:https://turq.readthedocs.io/en/latest/

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

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

另外,我建议尝试stub而不是mock,并使用以下Python模块:mock-serverhttps://pypi.python.org/pypi/mock-server/0.3.7 您需要创建包含相应的预填充JSON响应的目录布局,并添加一些样板代码,以便使mock-server响应为'本地主机: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做了大量的项目,大大小小的测试,测试了客户端和服务器端。在

相关问题 更多 >