Фляга-сокетио, фляга-sqlalchemy и pytest-селен
Я пытаюсь добавить тесты Selenium в приложение Flask, которое сильно зависит от Flask-SocketIO. Однако при доступе к странице из браузера Selenium, который выполняет запрос к базе данных, в окне браузера появляется следующее сообщение об ошибке:
Traceback (most recent call last):
File "/home/bbbart/.virtualenvs/heistmanager/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 346, in connection
return self.__connection
AttributeError: 'Connection' object has no attribute '_Connection__connection'
During handling of the above exception, another exception occurred:
[...]
File "/home/bbbart/.virtualenvs/heistmanager/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 429, in _revalidate_connection
raise exc.ResourceClosedError("This Connection is closed")
sqlalchemy.exc.ResourceClosedError: This Connection is closed
bbbart
мое имя пользователяheistmanager
это название приложения- Я использую следующий пакет PyPI (среди прочих):
- Колба == 0.12.2
- SQLAlchemy == 1.1.13
- Колба-SQLAlchemy==2.2
- Колба-SocketIO==2.9.2
- Колба-Script == 2.0.5
- eventlet == 0.21.0
- pytest == 3.2.1
- pytest-колбу == 0.10.0
- pytest-селен ==1.11.0
У меня есть обширный набор pytest для этого приложения, который уже работает отлично, включая тесты, основанные на базе данных, и тесты с использованием client
крепеж из пытест-колбы. Эта ошибка появляется только при внешнем запуске сервера для Selenium.
Чтобы приложение правильно запускалось с поддержкой веб-сокетов, я переопределил класс LiveServer и фикстуру live_server из pytest_flask следующим образом (это необходимо?) В conftest.py
:
class LiveSocketIOServer(LiveServer):
"""The helper class to manage live socketio server. Handles creation and
stopping of the application in a separate process.
:param app: The application to run.
:param port: The port to run application.
"""
def start(self, socketio):
"""Start application in a separate process."""
def worker(app, port):
return socketio.run(app, port=port, use_reloader=False)
self._process = multiprocessing.Process(
target=worker,
args=(self.app, self.port)
)
self._process.start()
# We must wait for the server to start listening with a maximum
# timeout of 5 seconds.
timeout = 5
while timeout > 0:
time.sleep(1)
try:
urlopen(self.url())
timeout = 0
except BaseException:
timeout -= 1
def __repr__(self):
return '<LiveSocketIOServer listening at %s>' % self.url()
а также
@pytest.yield_fixture(scope='function')
def live_server(request, app, monkeypatch):
"""Run application in a separate process.
When the ``live_server`` fixture is applied, the ``url_for`` function
works as expected::
def test_server_is_up_and_running(live_server):
index_url = url_for('index', _external=True)
assert index_url == 'http://localhost:5000/'
res = urllib2.urlopen(index_url)
assert res.code == 200
"""
# Find an open port
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('', 0))
port = sock.getsockname()[1]
sock.close()
server_name = app.config['SERVER_NAME'] or 'localhost'
monkeypatch.setitem(app.config, 'SERVER_NAME',
_rewrite_server_name(server_name, str(port)))
server = LiveSocketIOServer(app, port)
if request.config.getvalue('start_live_server'):
server.start(SOCKETIO)
yield server
server.stop()
Мой тест выглядит так:
@pytest.mark.usefixtures('live_server')
class TestSockets:
"""Tests for socket.io (with selenium)."""
@pytest.mark.nondestructive
def test_admin_login(self, config, selenium):
selenium.get(url_for('main.index', _external=True))
assert 'Hello stranger!' in selenium.page_source
selenium.find_element_by_link_text('Log in to administer').click()
assert '<h1>Login</h1>' in selenium.page_source
selenium.find_element_by_name('username').send_keys(
config['USERNAME_ADMIN'])
selenium.find_element_by_name('password').send_keys('*******')
selenium.find_element_by_name('submit').click()
assert 'Logged in as %s' % config['USERNAME_ADMIN'] in selenium.page_source
(Вы можете узнать некоторые элементы из отличной демонстрации Flasky Мигеля Гринберга.:-))
Ошибка выше поднимается после второго click
позвоните в предпоследнюю строку теста. Это впоследствии, конечно, делает assert
утверждение провалилось, но дело не в этом.
Тесты запускаются из команды диспетчера флеш-скриптов:
APP = create_app(os.getenv('HEIST_CONFIG') or 'default')
MANAGER = Manager(APP)
@MANAGER.command
def test():
"""Run the tests."""
import pytest
pytest.main(['--driver', 'Chrome',
os.path.join(basedir, 'tests')])
Я потратил несколько часов, пытаясь выяснить, почему соединение SQLAlchemy закрыто (или даже не открыто?). Я добавил в приборы все виды БД, но это никогда не казалось правильным, а также никогда не работало.
Я заблудился и был бы признателен за любой указатель здесь на решение. Спасибо!