Ruff9
Ruff9

Reputation: 1212

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

I'm struggling to setup my test runner for a new Django project. I want to use these libraries :

And I want it to work with Docker. I got a head start with that answer : https://stackoverflow.com/a/58447497/3219759 but I can't make it work.

Here is my 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:

And to finish the setup, I have these fixtures in the conftest.py file :

@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'

and this in settings.py:

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

I have a basic view that list all the profiles, and a template to match. This feature is working on my local server.

The corresponding test:

@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

The ProfileFactory is set up with factoryboy, and it seems to work fine.

I launch the test suite with

$ docker compose run --rm web pytest

And the test fails :

_______________________ 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

Basically the test can't find the profile in the browser, the screenshot is confirming that with a 'no profiles yet' message on the page.

But when I setup a breakpoint there :

@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

I can easily find the profile :

>>>>>>>>>>>>>>>>>>> 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>]>

So, factoryboy is generating a profile, which I can see with the breakpoint. But this data is not shown in the browser generated by selenium, and the test fails.

There must be something wrong in the Docker setup, feels like there is two test databases, one used by selenium and the other by pytest. There is a bad connection somewhere.

Edit

Just found this message in the pytest warnings that are displayed after the failure :

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')

Upvotes: 1

Views: 128

Answers (1)

Ruff9
Ruff9

Reputation: 1212

Thanks to @mariodev comment, I found a way to make it work.

I used this basic mark with my test :

@pytest.mark.django_db

I finally made it work with the transactional option :

@pytest.mark.django_db(transaction=True)

Upvotes: 1

Related Questions