简体   繁体   English

在 docker 中使用 selenium 运行 django 测试

[英]Running django tests with selenium in docker

For executing tests I usually run a separate container with:为了执行测试,我通常运行一个单独的容器:

docker-compose run --rm web /bin/bash

Where web is a container with django.其中 web 是带有 Django 的容器。 From a shell I execute py.test from time to time.我不时从 shell 执行 py.test。

In order to be able to reach selenium from a container with django and to allow the browser from selenium container to reach django's liveserver I decided to use "net" parameter which allows containers to share net.为了能够从带有 django 的容器访问 selenium 并允许来自 selenium 容器的浏览器访问 django 的 liveserver,我决定使用“net”参数,允许容器共享网络。 So I added it to the yml:所以我将它添加到 yml 中:

selenium:
    image: selenium/standalone-firefox
    net: "container:web"

Unfortunately this does not work.不幸的是,这不起作用。 I do not see 4444 port in my django container.我在 django 容器中没有看到 4444 端口。

It only works if instead of net:"container:web" I specify an autogenerated container's name, like net:"container:project_web_run_1" .它仅在我指定自动生成的容器名称而不是net:"container:web"时才有效,例如net:"container:project_web_run_1"

Also I tried instead of docker-compose run --rm .... use docker-compose up --no-deps changing command parameter to py.test functional_tests but that did not work either.我也尝试代替docker-compose run --rm ....使用docker-compose run --rm .... docker-compose up --no-depscommand参数更改为py.test functional_tests但这也不起作用。

Is this the right of using selenium with containers?这是在容器中使用硒的权利吗?

Here is how I do it.这是我如何做到的。 The basic problem is that docker-compose run will generate a different hostname (project_container_run_x) where x is hard to know for sure.基本问题是 docker-compose run 将生成不同的主机名(project_container_run_x),其中 x 很难确定。 I ended up just going off of ip address.我最终只是离开了IP地址。 I'm also ensuring DEBUG is False otherwise I get a bad request.我还确保 DEBUG 为 False 否则我会收到错误的请求。

I'm using StaticLiveServerTestCase like this:我正在像这样使用 StaticLiveServerTestCase:

import os
import socket

os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = '0.0.0.0:8000'

class IntegrationTests(StaticLiveServerTestCase):
    live_server_url = 'http://{}:8000'.format(
        socket.gethostbyname(socket.gethostname())
    )

    def setUp(self):
        settings.DEBUG = True
        self.browser = webdriver.Remote(
            command_executor="http://selenium:4444/wd/hub",
            desired_capabilities=DesiredCapabilities.CHROME
        )

    def tearDown(self):
        self.browser.quit()
        super().tearDown()

    def test_home(self):
        self.browser.get(self.live_server_url)

My docker-compose file has this for selenium and extends the web container (where django is running).我的 docker-compose 文件有这个用于 selenium 并扩展了 web 容器(运行 django 的地方)。 Port 5900 is open for VNC.端口 5900 对 VNC 开放。 I like to keep this isolated in something like docker-compose.selenium.yml我喜欢将其隔离在 docker-compose.selenium.yml 之类的东西中

version: '2'
services:
  web:
    environment:
      SELENIUM_HOST: http://selenium:4444/wd/hub
      TEST_SELENIUM: 'yes'
    depends_on:
      - selenium

  selenium:
    image: selenium/standalone-chrome-debug
    ports:
      - "5900:5900"

I can run tests like我可以运行测试

docker-compose run --rm web ./manage.py test

So my web container is accessing selenium via the "selenium" host.所以我的 web 容器通过“selenium”主机访问 selenium。 Selenium then accesses the web container by ip address which is determined on the fly. Selenium 然后通过动态确定的 IP 地址访问 Web 容器。

Another gotcha is that it's tempting to just use "web" as the hostname.另一个问题是,很容易只使用“web”作为主机名。 If your docker-compose run command starts up a separate web container - this will appear to work.如果您的 docker-compose run 命令启动了一个单独的 Web 容器 - 这似乎可以工作。 However it won't be using your test database, making for not a great test.但是它不会使用您的测试数据库,因此不是一个很好的测试。

For anyone running pytest, and possibly pytest-splinter (Selenium wrapper)对于任何运行 pytest 的人,可能还有 pytest-splinter(Selenium 包装器)

version: '3'
services:
  db:
    image: postgres
  django:
    build: .
    ports: 
      - "8000:8000"
    depends_on:
      - db
      - selenium
  selenium:
    image: selenium/standalone-firefox-debug:latest
    ports:
      - "4444:4444"   # Selenium
      - "5900:5900"   # VNC 

Define a conftest.py in your root directory to make these fixtures available to all your tests在您的根目录中定义一个 conftest.py 以使这些装置可用于您的所有测试

import socket

import pytest
from pytest_django.live_server_helper import LiveServer


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


@pytest.fixture(autouse=True, scope='function')
def _test_server_helper(request):
    """
    Configures test_server fixture so you don't have to mark
    tests with @pytest.mark.django_db
    """
    if "test_server" not in request.fixturenames:
        return

    request.getfixturevalue("transactional_db")


# Settings below here are exclusive to splinter,
# I'm just overriding the default browser fixture settings
# If you just use selenium, no worries, just take note of the remote url and use 
# it wherever you define your selenium browser

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

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

Don't forget to set ALLOWED_HOSTS in your config file:不要忘记在配置文件中设置 ALLOWED_HOSTS:

if env('USE_DOCKER') == 'yes':
    import socket

    ALLOWED_HOSTS = [socket.gethostbyname(socket.gethostname())]

# or just
ALLOWED_HOSTS = ['*']

Then just test away!那就试试吧!

from django.urls import reverse


def test_site_loads(browser, test_server):
    browser.visit(test_server.url + reverse('admin:index'))

I have just specified host='web' for LiveServerTestCase .我刚刚为LiveServerTestCase指定了host='web' Here is my working solution.这是我的工作解决方案。

test.py

from django.test import LiveServerTestCase
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

class FunctionalTestCase(LiveServerTestCase):
    host = 'web'
    def setUp(self):
        self.browser = webdriver.Remote(
            command_executor="http://selenium:4444/wd/hub",
            desired_capabilities=DesiredCapabilities.FIREFOX
        )

    def test_user_registration(self):
        self.browser.get(self.live_server_url)
        self.assertIn('Django', self.browser.title)

    def tearDown(self):
        self.browser.close()

docker-compose.yml

version: '3'
services:
  db:
    image: postgres
  web:
    build: .
    ports: 
      - "8000:8000"
    depends_on:
      - db
      - selenium
  selenium:
    image: selenium/standalone-firefox

Remember you have to have to install the selenium in your docker image for this to work:请记住,您必须在 docker 映像中安装selenium才能使其正常工作:

$ docker-compose exec web bash
> pip install selenium
...
> pip freeze > ../requirements.txt
> exit
$ ...

In my case, the "web" container runs only one command, which is bash -c "sleep infinity" .就我而言,“web”容器只运行一个命令,即bash -c "sleep infinity"

Then, I start the whole stack with docker-compose up -d .然后,我使用docker-compose up -d启动整个堆栈。

Then, I use docker-compose exec web bash -c "cd /usr/src/app && tox" , for example.然后,例如,我使用docker-compose exec web bash -c "cd /usr/src/app && tox"

This way, my web host is accessible from selenium , always under the same name.这样,我的web主机就可以从selenium访问,始终使用相同的名称。

Using docker-compose run web ... generates new (predictable, but still) host name every single time.使用docker-compose run web ...每次都会生成新的(可预测的,但仍然是)主机名。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM