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