Получение настроек и конфигурации из файла 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 5

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.