简体   繁体   中英

Selenium login test doesn't accept pytest fixtures for login or refuses to connect

I'm getting desperate because I can't seem to find a solution for what I thought would be used by everyone out there.

I want to test a simple login with selenium and pytest with a live_server url. According to pytest-django doc, a simple fixture called live_server should do the trick ( https://pytest-django.readthedocs.io/en/latest/helpers.html#live-server ).

Unfortunately when I pass this fixture to my test and try to visit my website with it I get:

localhost refused to connect

for example for this:

    def test_selenium(self, live_server, create_staff_user_in_db):
        browser = webdriver.Remote(
            command_executor='http://selenium:4444/wd/hub',
            desired_capabilities=DesiredCapabilities.CHROME,
        )

        browser.get(live_server.url)

Leveraging this question Selenium unable to login to Django LiveServerTestCase I am trying to use a different server-fixture which then connects me and I can see my website in my VNC viewer.

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

But with this I am now not able to login to my website if I create users in the db with my fixtures. Even though it seems like users are actually created. So my test now looks like:


  def test_selenium(self, test_server, create_staff_user_in_db):
        browser = webdriver.Remote(
            command_executor='http://selenium:4444/wd/hub',
            desired_capabilities=DesiredCapabilities.CHROME,
        )

        browser.get(f"{live_server.url}/login")
        input_username = browser.find_element_by_name('username')
        input_password = browser.find_element_by_name('password')
        input_username.send_keys('testuserstaff')
        input_password.send_keys('mypasswordstaff')
        browser.find_element_by_xpath(
            '//*[@id="page-top"]/div[1]/div/div/div/div/form/button'
        ).click()

And my user fixture is:

@pytest.fixture()
def create_staff_user_in_db():
    User = get_user_model()
    staff_user = User.objects.create_user(
        username="testuserstaff",
        password="mypasswordstaff",
    )
    staff_user.is_staff = True
    staff_user.save()

    return staff_user

The tests visits my login page and fails to log in. I am 100% certain that I am using the right credentials.

If I print out for debugging I also can verify that my user is in my db:

print(u.username) ==> testuserstaff
print(u.password) ==> igetthehashofthepassword
print(User.objects.count()) ==> 1 

Thus I assume the db that is being created by pytest is filled with this user I pass as fixture. Now somehow my live server is either not using the db or not recognising the fixtures.

It must be connected to the live_server/test_server fixture. I would be supergrateful for any help. I feel like I am so close but I just can't seem to find out why it doesn't log in.

What else have I tried: 1) Login in with users that are in my development db. No success. So I am wondering: What db is then be used?

2) I tried to setup pytest-selenium library. If I try to load a page I get selenium.common.exceptions.SessionNotCreatedException: Message: Unable to create session

3) I tried to work with StaticLiveServerCase, but this is not an option since I need to pass fixtures as an argument.

4) I searched everywhere on the web and cannot find anything else

Also good to know:

I am running this in a docker environment (happy to share if it helps)

So my stack is basically: Docker, Django, Pytest, selenium

Again, would really appreciate a hand here. Thanks so much in advance

EDIT:

My docker-compose file:

version: '3'

volumes:
  local_postgres_data: {}
  local_postgres_data_backups: {}

services:
  django:
    build:
      context: .
      dockerfile: ./compose/local/django/Dockerfile
    image: mywebsite_local_django
    depends_on:
      - postgres
    volumes:
      - .:/app
    env_file:
      - ./.envs/.local/.django
      - ./.envs/.local/.postgres
    ports:
      - "8000:8000"
    command: /start

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

  postgres:
    build:
      context: .
      dockerfile: ./compose/production/postgres/Dockerfile
    image: mywebsite_production_postgres
    volumes:
      - local_postgres_data:/var/lib/postgresql/data
      - local_postgres_data_backups:/backups
    env_file:
      - ./.envs/.local/.postgres

Turning the comment into an answer: by default, the tests avoid running db queries to not to slow down the test run, so User.objects.create_user doesn't write to database. To ensure db commits, use the @pytest.mark.django_db(transaction=True) marker in tests or the transactional_db fixture in your fixtures:

@pytest.fixture()
def create_staff_user_in_db(transactional_db):
    staff_user = User.objects.create_user(...)
    ...

I assume that the live_server fixture of django uses transactional_db automatically but that the test_server doesn't?

Exactly - live_server implies the transactional mode, but the custom impl of test_server doesn't. Explicitly requesting the transactional_db fixture in test_server should fix it:

@pytest.fixture
def test_server(request) -> LiveServer:
    request.getfixturevalue("transactional_db")
    ...

BTW if you just want to apply the live server address dynamically, you shouldn't need to define your own fixture; writing config values should suffice. Example:

# conftest.py

def pytest_configure(config):
    config.option.liveserver = socket.gethostbyname(socket.gethostname())

This value will then be used by the builtin live_server fixture. Put the conftest.py into your project or tests root dir to make pytest find the hookimpl early enough for it to be applied.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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