Feedback on my pytest / Selenium project

Hello,

I hope the community here could give me some feedback on on my first practice project using pytest and Selenium. The project contains a separate file for the pytest fixture and then two separate tests.

Some notes on my decisions:

I chose not to use page objects mostly because I prioritize visibility, and I prefer that I all code for a test be neatly visible in one file.

My locators are maybe odd. I tried xpath relative paths but I must have done something wrong, so I settled for absolute paths which works just fine. But this kind of locator is far from readable of course. I have more to learn about locators.

I have learned that flakiness is a thing. I have read about flaky tests from tutorials and blogs but now I know what it means. This test fails on my machine every now and then. (Timeouts when another browser is open or element not interactable) It would be easier to deal with a consistent fail rather than one that appears occasionally. I don’t expect anyone to run my test files, but if there is anything in the code that stands out as a likely culprit please let me know.

One line of code is identical in both tests. A constant:

URL = “https://www.oodihelsinki.fi/en/

On one hand I understand repeated code is a code smell, on the other hand the two tests are isolated from one another. Let me know if there is a better way to declare this constant.

All in all I enjoy thinking trough a test case and see Selenium execute it with my own eyes. Working with Selenium seems to be a lot about making deliberate and thoughtful decisions on what to test and why.

Anyhow. If anyone spots a particularly rankling code smell let me know. All feedback is greatly appreciated.

Code below:

conftest.py

import pytest
from selenium.webdriver import Chrome


\# Initialize and quit handled by a pytest fixture
@pytest.fixture
*def* browser(*scope*="module"):
  driver = Chrome()
  
  driver.implicitly_wait(15)
  yield driver
  driver.quit()


test_oodi_FAQS.py

from selenium.webdriver.common.by import By
from selenium.common.exceptions import ElementClickInterceptedException

from selenium.common.exceptions import NoSuchElementException

*def* test_oodi_faq_interaction(*browser*):
  \# GIVEN The Oodi homepage is displayed
  URL = "https://www.oodihelsinki.fi/en/"

  browser.get(URL)
  browser.implicitly_wait(20)

  \# WHEN the user clicks on FAQ

  faq_search_window = browser.find_element(By.LINK_TEXT, "Questions and answers")

  browser.implicitly_wait(20)

  while True:
​    try:
​      faq_search_window.click()
​      break
​    except ElementClickInterceptedException:
​      browser.execute_script("window.scrollBy(0,-100);")

  \# THEN the FAQ page is displayed

  assert browser.current_url == "https://www.oodihelsinki.fi/en/faq/"

  browser.implicitly_wait(20)

  \# AND the fact about dogs in the library is displayed

  xpath = "/html/body/div[1]/div[1]/div/div/section[2]/div[16]/div/div[1]/h3"

  *def* element_present_check():
​    try:
​      browser.find_element(By.XPATH, xpath)
​      return True
​    except NoSuchElementException:
​      return False

  assert element_present_check() is True

test_oodi_facilities.py

from selenium.webdriver.common.by import By

\# from selenium.common.exceptions import ElementClickInterceptedException

*def* test_oodi_facilities_interaction(*browser*):
  \# Given The Oodi homepage is displayed
  URL = "https://www.oodihelsinki.fi/en/"

  browser.get(URL)
  browser.implicitly_wait(20)

  \# WHEN the user clicks the plus buttton to expand 'Services and facilities'
  \# AND the user clicks on 'facilities'

  plus_button_xpath = ("/html/body/section/div[1]/div[1]/nav/ul/li[2]/button/div/div[2]")

  facilities_xpath = "/html/body/section/div[1]/div[1]/nav/ul/li[2]/ul/li/ul/li[2]/a"

  browser.implicitly_wait(20)
  plus_button = browser.find_element(By.XPATH, plus_button_xpath)
  plus_button.click()
  browser.implicitly_wait(20)
  facilities = browser.find_element(By.XPATH, facilities_xpath)
  facilities.click()
  browser.implicitly_wait(20)

  \# THEN the user is on the facilities page
  assert (browser.current_url == "https://www.oodihelsinki.fi/en/services-and-facilities/facilities/")
1 Like

Hello!

This is a great attempt. I am by no means an expert and am still learning as I go myself, but I have spotted a couple of things I would like to question.

browser.implicitly_wait(seconds)
What exactly are you waiting for?
Are you waiting for an element to appear on the screen before the script attempts to click? If so, you can use wait untils. The main reason for this is performance. Your script will be waiting an arbitrary number of seconds which a) may be too long or b) not long enough so it is good practise to try not to rely on implicit waits. I would personally fail a code review if a person favoured waiting a set number of seconds instead of for an element to load without a justification as to why - just a comment in the code is enough.

xpaths
You have commented that you have had to use absolute paths. This has happened to me before and I created my own HTML tags purely for automation. When I inspected the HTML of a page then it would have ‘selenium=“submit_button”’ for example. Appreciate you may not be able to do that in this scenario, but just thought I would tell you how I worked around it in work.

Let me know if you have any questions :slightly_smiling_face:

Hello @froberts and thanks a lot for your feedback on my script.

The implicit waits in my scripts are indeed there because I am waiting for elements to load (I haven’t yet practiced Selenium explicit waits at all).

I have a fairly slow internet connection at present and my decision on using long implicit waits had to do with me noticing how the page loads loaded quite slowly combined with my test failing occasionally.

Often it was a case of me noticing a test failed, next I added an implicit wait and the next time the test ran successfully. But with this approach I am not really concluding what fails and why, so your suggestion about being clear about why I use implicit waits is spot on. I should also look into if explicit waits are preferable.

As for the xpath: Your solution where you add custom html attributes is very interesting and I may use that if I am testing a page where I have access to edit the html code. I imagine that as a last resort when there is no id or a relative xpath that can be used. I found a course on test automation university that deals specifically about html locators. I think I will dig deeper into that course, and also learn more about waits, before my next practice project.

1 Like