Получение настроек и конфигурации из файла INI для функционального тестирования пирамиды
В реальном приложении пирамиды он не работает на docs http://docs.pylonsproject.org/projects/pyramid//en/latest/narr/testing.html :
class FunctionalTests(unittest.TestCase):
def setUp(self):
from myapp import main
app = main({})
Исключение:
Traceback (most recent call last):
File "C:projectsmyapptestsmodeltask_dispatcher_integration_test.py", line 35, in setUp
app = main({})
File "C:projectsmyappmyapp__init__.py", line 207, in main
engine = engine_from_config(settings, 'sqlalchemy.')
File "C:projectsmyappvelibsite-packagessqlalchemyengine__init__.py", line 407, in engine_from_config
url = options.pop('url')
KeyError: 'url'
Причина тривиальна: пустой словарь передается в main
, в то время как кажется, что при запуске реального приложения (из __init__.py
) он получает settings
предварительно заполненный значениями из [app:main]
раздела development.ini
/ production.ini
:
settings {'ldap_port': '4032', 'sqlalchemy.url': 'postgresql://.....}
Есть ли какой-то способ легко восстановить settings
из файла .ini
для функционального тестирование?
4 ответа:
pyramid.paster.get_appsettings
это единственное, что вам нужно:from pyramid.paster import get_appsettings settings = get_appsettings('test.ini', name='main') app = main(settings)
Что
test.ini
может включать все настройки другого файла.ini
легко, как это:[app:main] use = config:development.ini#main
И затем вам нужно только переопределить те ключи, которые меняются (я думаю, вы хотели бы скорее протестировать против отдельной БД):
[app:main] use = config:development.ini#main sqlalchemy.uri = postgresql://....
Да, есть, хотя легкий является предметом обсуждения.
Я использую следующее
py.test
тестовое приспособление, чтобы сделать опцию--ini
переданной тестам. Однако этот подход ограниченpy.test
тестовым бегуном, так как другие тестовые бегуны не обладают такой гибкостью.Также у моего
test.ini
есть специальные настройки, такие как отключение исходящей почты и вместо этого печать ее на терминал и проверка доступного бэклога.@pytest.fixture(scope='session') def ini_settings(request): """Load INI settings for test run from py.test command line. Example: py.test yourpackage -s --ini=test.ini :return: Adictionary representing the key/value pairs in an ``app`` section within the file represented by ``config_uri`` """ if not getattr(request.config.option, "ini", None): raise RuntimeError("You need to give --ini test.ini command line option to py.test to find our test settings") # Unrelated, but if you need to poke standard Python ConfigParser do it here # from websauna.utils.configincluder import monkey_patch_paster_config_parser # monkey_patch_paster_config_parser() config_uri = os.path.abspath(request.config.option.ini) setup_logging(config_uri) config = get_appsettings(config_uri) # To pass the config filename itself forward config["_ini_file"] = config_uri return config
Тогда я могу настроить приложение (обратите внимание, что здесь пирамида.Пастер.бутстреп-анализа снова конфигурационный файл:
@pytest.fixture(scope='session') def app(request, ini_settings, **settings_overrides): """Initialize WSGI application from INI file given on the command line. TODO: This can be run only once per testing session, as SQLAlchemy does some stupid shit on import, leaks globals and if you run it again it doesn't work. E.g. trying to manually call ``app()`` twice:: Class <class 'websauna.referral.models.ReferralProgram'> already has been instrumented declaratively :param settings_overrides: Override specific settings for the test case. :return: WSGI application instance as created by ``Initializer.make_wsgi_app()``. """ if not getattr(request.config.option, "ini", None): raise RuntimeError("You need to give --ini test.ini command line option to py.test to find our test settings") data = bootstrap(ini_settings["_ini_file"]) return data["app"]
Кроме того, настройка функционального тестового сервера:
import threading import time from wsgiref.simple_server import make_server from urllib.parse import urlparse from pyramid.paster import bootstrap import pytest from webtest import TestApp from backports import typing #: The URL where WSGI server is run from where Selenium browser loads the pages HOST_BASE = "http://localhost:8521" class ServerThread(threading.Thread): """ Run WSGI server on a background thread. Pass in WSGI app object and serve pages from it for Selenium browser. """ def __init__(self, app, hostbase=HOST_BASE): threading.Thread.__init__(self) self.app = app self.srv = None self.daemon = True self.hostbase = hostbase def run(self): """Open WSGI server to listen to HOST_BASE address """ parts = urlparse(self.hostbase) domain, port = parts.netloc.split(":") self.srv = make_server(domain, int(port), self.app) try: self.srv.serve_forever() except Exception as e: # We are a background thread so we have problems to interrupt tests in the case of error import traceback traceback.print_exc() # Failed to start self.srv = None def quit(self): """Stop test webserver.""" if self.srv: self.srv.shutdown() @pytest.fixture(scope='session') def web_server(request, app) -> str: """py.test fixture to create a WSGI web server for functional tests. :param app: py.test fixture for constructing a WSGI application :return: localhost URL where the web server is running. """ server = ServerThread(app) server.start() # Wait randomish time to allows SocketServer to initialize itself. # TODO: Replace this with proper event telling the server is up. time.sleep(0.1) assert server.srv is not None, "Could not start the test web server" host_base = HOST_BASE def teardown(): server.quit() request.addfinalizer(teardown) return host_base
На случай, если кто-то еще не получит ответ @antti-haapala сразу:
Создайте тест.ini заполняется:
[app:main] use = config:development.ini#main
(На самом деле этот шаг не нужен. Вы также можете продолжать свое развитие.ini и использовать его вместо теста.ini в следующем коде. Отдельный тест.однако ini может быть полезен, если вы хотите отдельные настройки для тестирования)
В вашем tests.py добавить:
from pyramid.paster import get_appsettings settings = get_appsettings('test.ini', name='main')
И заменить
app = TestApp(main({}))
С
app = TestApp(main(global_config = None, **settings))
Уместным для этого ответа было следующее: следующий комментарий: пирамида не запускается, когда webtest и sqlalchemy используются вместе
На самом деле, вам не нужно импортировать get_appsettings, просто добавьте параметры, подобные этому:
class FunctionalTests(unittest.TestCase): def setUp(self): from myapp import main settings = {'sqlalchemy.url': 'sqlite://'} app = main({}, **settings)
Вот источник: функциональный тест, он находится во втором блоке кода, строка 31.