Docker 中的 Pytest 和 Selenium:测试套件使用的数据库错误?

Pytest and Selenium in Docker : wrong database used for the test suite?

提问人:Ruff9 提问时间:11/16/2023 最后编辑:Ruff9 更新时间:11/22/2023 访问量:53

问:

赏金将在 2 天后到期。这个问题的答案有资格获得 +50 声望赏金。Ruff9引起人们对这个问题的更多关注
寻找一种干净的方法,使用本地 Docker 设置在 Django 应用上进行集成测试

我正在努力为一个新的 Django 项目设置我的测试运行程序。我想使用这些库:

  • 姜戈 4.2
  • pytest-django 4.6.0
  • pytest-分裂 3.3.2

我希望它能与 Docker 一起使用。我的答案是:https://stackoverflow.com/a/58447497/3219759,但我不能让它工作。

这是我的:docker-compose.yml

version: '3.3'

services:
  web:
    build: jam
    restart: always
    command: python manage.py runserver 0.0.0.0:8000
    environment:
      - USE_DOCKER=True
    env_file:
      - .env
    ports:
      - "127.0.0.1:8000:8000"
    volumes:
      - .:/code
    links:
      - db
    depends_on:
      - db
      - selenium

  db:
    image: postgres
    environment:
      - POSTGRES_USER=xxx
      - POSTGRES_PASSWORD=xxx
      - POSTGRES_DB=xxx
    ports:
      - "127.0.0.1:5432:5432"
    volumes:
      - pg_data:/var/lib/postgresql/data

  selenium:
    image: selenium/standalone-firefox:4.9.1
    ports:
      - "4444:4444"   # Selenium
      - "5900:5900"   # VNC

volumes:
  pg_data:

为了完成设置,我在文件中有这些夹具:conftest.py

@pytest.fixture(scope='session')
def test_server() -> LiveServer:
    addr = socket.gethostbyname(socket.gethostname())
    server = LiveServer(addr)
    yield server
    server.stop()

@pytest.fixture(scope='session')
def splinter_webdriver():
    return 'remote'

@pytest.fixture(scope='session')
def splinter_remote_url():
    return 'http://selenium:4444/wd/hub'

这在:settings.py

if env('USE_DOCKER') == 'yes':
    import socket
    ALLOWED_HOSTS = [socket.gethostbyname(socket.gethostname())]

我有一个列出所有配置文件的基本视图,以及一个要匹配的模板。此功能在我的本地服务器上工作。

相应的测试:

@pytest.mark.django_db
class TestIndexPage:
    def test_profile_list(self, browser, test_server):
        profile = ProfileFactory(description='first description')

        browser.visit(test_server.url + reverse('index', current_app='Profiles'))

        assert browser.is_text_present('first description') is True

ProfileFactory 是用 factoryboy 设置的,它似乎工作正常。

我用

$ docker compose run --rm web pytest

测试失败:

_______________________ TestIndexPage.test_profile_list ________________________

self = <profiles.tests.test_index.TestIndexPage object at 0x7fd3a15141a0>
browser = <splinter.driver.webdriver.remote.WebDriver object at 0x7fd3a0034680>
test_server = <LiveServer listening at http://172.31.0.4:49073>

    def test_profile_list(self, browser, test_server):
        profile1 = ProfileFactory(description='first description')

        browser.visit(test_server.url + reverse('index', current_app='Profiles'))
    
>       assert browser.is_text_present('first description') is True
E       AssertionError: assert False is True
E        +  where False = <bound method BaseWebDriver.is_text_present of <splinter.driver.webdriver.remote.WebDriver object at 0x7fd3a0034680>>('first description')
E        +    where <bound method BaseWebDriver.is_text_present of <splinter.driver.webdriver.remote.WebDriver object at 0x7fd3a0034680>> = <splinter.driver.webdriver.remote.WebDriver object at 0x7fd3a0034680>.is_text_present

profiles/tests/test_index.py:13: AssertionError

基本上测试在浏览器中找不到配置文件,屏幕截图正在确认页面上的“尚无配置文件”消息。

但是当我在那里设置断点时:

@pytest.mark.django_db
class TestIndexPage:
    def test_profile_list(self, browser, test_server):
        profile = ProfileFactory(description='first description')
        breakpoint()
        browser.visit(test_server.url + reverse('index', current_app='Profiles'))

        assert browser.is_text_present('first description') is True

我可以很容易地找到个人资料:

>>>>>>>>>>>>>>>>>>> PDB set_trace (IO-capturing turned off) >>>>>>>>>>>>>>>>>>>>
> /code/profiles/tests/test_index.py(11)test_profile_list()
-> browser.visit(test_server.url + reverse('index', current_app='Profiles'))
(Pdb) from profiles.models import Profile
(Pdb) Profile.objects.count()
1
(Pdb) Profile.objects.all()
<QuerySet [<Profile: george36>]>

因此,factoryboy 正在生成一个配置文件,我可以通过断点看到它。但是这个数据没有显示在selenium生成的浏览器中,测试失败。

Docker 设置中一定有问题,感觉有两个测试数据库,一个由 selenium 使用,另一个由 pytest 使用。某处连接不良。

编辑

刚刚在失败后显示的pytest警告中发现了此消息:

profiles/tests/test_index.py::TestIndexPage::test_profile_list
  /code:0: PytestWarning: Error when trying to teardown test databases:

OperationalError('database "test_tutorial" is being accessed by other users\nDETAIL:  There is 1 other session using the database.\n')
django docker selenium-webdriver pytest 分裂

评论

0赞 mariodev 11/20/2023
1. 你有没有试过用?2. 您是否有多个设置模块(单独的一个用于测试)?如果有 - 您是否配置正确?@pytest.fixture(scope='session')create_dbDJANGO_SETTINGS_MODULE
0赞 Ruff9 11/20/2023
你在哪里看到?我不明白你这是什么意思。而且我只有一个设置模块。有一个专门用于测试的好做法吗?我确实有一个包含该属性的模块。create_dbpytest.iniDJANGO_SETTINGS_MODULE
0赞 mariodev 11/20/2023
对不起,我的意思是我的错。我试图弄清楚这是否以某种方式导致单独创建数据库。@pytest.mark.django_db
0赞 Ruff9 11/20/2023
你做到了:)我刚刚找到了解决方案,经过 3 天的挣扎T_T事实证明,我确实尝试使用事务请求,但语法过时(恐怕基于旧的 stackoverflow 帖子...... )在您的评论之后,我再次阅读了该特定标记上的文档,并使用了新语法。如果你想回答这个问题,我可以给你所有你应得的荣誉:)@pytest.mark.django_db(transaction=True)
1赞 mariodev 11/21/2023
没关系。您可以继续回答自己的问题,因为您毕竟:)完成了所有工作。我很高兴我能向你推荐正确的解决方案。

答:

1赞 Ruff9 11/22/2023 #1

多亏了@mariodev评论,我找到了一种让它工作的方法。

我在测试中使用了这个基本标记:

@pytest.mark.django_db

我终于让它与事务选项一起工作:

@pytest.mark.django_db(transaction=True)