I have a pytest script that has multiple classes which each have a set of test. Currently, each test within each class has the same TimeoutException defined. For example,
class Test1:
def test_1:
try:
"do something"
except TimeoutException:
"handle exception"
def test_2:
try:
"do something"
except TimeoutException:
"handle exception"
class Test2:
def test_3:
try:
"do something"
except TimeoutException:
"handle exception"
The "handle exception" part is where I have the same code for each module. I was wondering if there was a more pythonic way to do this. It seems sloppy to have the same lines pasted within each module for my TimeoutException handler.
Any help is appreciated and if more information is desired please let me know.
Thanks in advance!
Maybe try to add some explicit waits for functions you want to handle. Explicit waits are dedicated especially for test cases, when you have individual logic for every test. I suppose your issue concerns selenium, as I see you tagged your question. You can define it in every test, for every element you want to wait for.
To implement such explicit wait, you can use the most popular tool designed for WebDrivers: WebdriverWait. You can set the Timeout duration and the conditions defined when the TimeoutException needs to be raised.
Here is and example from official Selenium site:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Firefox()
driver.get("http://somedomain/url_that_delays_loading")
try:
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "myDynamicElement"))
)
finally:
driver.quit()
I modified this case a little bit according to your code and here is an example of customizing TimeoutException (in the simplest way ignoring Page Object Model):
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Firefox()
driver.get("http://somedomain/url_that_delays_loading")
class Test1:
def test_1:
try:
element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "myDynamicElement")))
except TimeoutException:
"handle exception"
Another wait that is really awesome is the implicit wait, that sets overall timeout for all actions done by Selenium like this:
driver.implicitly_wait(10)
That means, the webdriver will wait ten seconds for an Webelement, and after that TimeoutException will be raised. However, this kind of wait should be treated as a global, case-independent wait.
I hope this answer helps.
Here you have a documentation how to do this:
https://selenium-python.readthedocs.io/waits.html
Related
I would like to switch up to two toggles on a website that expand or hide parts of a CMS tree (expanded picture for context) that does not have an API. They are on the same hierarchy level of the tree. The toggles are randomly set on the "hidden" or "expanded" view and I need the first one to be set to hidden and the second to be expanded.
Generally, my code works:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
try:
iframe_cms_subpages = WebDriverWait(driver, delay).until(EC.presence_of_element_located((By.XPATH, '/html/body/table/tbody/tr[2]/td[1]/iframe'))) # (unfortunately, I have to go with an abs xpath here)
print("iframe found!")
except TimeoutException:
print("Loading iframe took too long")
driver.switch_to.frame(iframe_cms_subpages)
try:
main_menu1 = WebDriverWait(driver, delay).until(EC.presence_of_element_located((By.XPATH, "//a[#class='npsLogic_nodeViewEntry_c_/main_menu1_npsLogic']/img[#alt='-']")))
print("main_menu1 open! Close it first")
main_menu1.click()
print("closed!")
except TimeoutException:
print("main_menu1 was either already closed or something went wrong.")
try:
main_menu2 = WebDriverWait(driver, delay).until(EC.presence_of_element_located((By.XPATH, "//a[#class='npsLogic_nodeViewEntry_e_/main_menu2_npsLogic']/img[#alt='+']")))
print("main_menu2 closed! Needs to be opened first")
main_menu2.click()
print("main_menu2 opened!")
except TimeoutException:
print("main_menu2 was either already open or something went wrong."
The "+" in the xpath is present if it is closed (hidden), for the "-" it is vice versa (expanded).
My issue: I have tested this in all four scenarios and the code only works if up to one of the two toggles has to be switched to have both be set correctly, i.e. if either both of them are open, both of them are closed or the first one is closed and second one open (correct settings right away). If both of them need to be switched, i.e. if the first one is open and the second one is closed, the second try statement throws a TimeOutException. Why does the second explicit wait not work if an action was performed based on the previous one?
Notes: I included the iframe because I thought it might be relevant; that explicit wait works. I noticed that there is a "c" in the first xpath and an "e" in the second, but that is part of the path for some reason, not a typo.
Do I perhaps have an incorrect understanding of how explicit waits work?
Every time one of the elements is clicked, the elements were not recognized by the webdriver anymore. This can be fixed by just switching back to parent frame and then back into the iframe again! I have also changed the explicit wait type to element_to_be_clickable in line with the recommendation of Greg:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
try:
iframe_cms_subpages = WebDriverWait(driver, delay).until(EC.element_to_be_clickable((By.XPATH, '/html/body/table/tbody/tr[2]/td[1]/iframe'))) # (unfortunately, I have to go with an abs xpath here)
print("iframe found!")
except TimeoutException:
print("Loading iframe took too long")
driver.switch_to.frame(iframe_cms_subpages)
try:
main_menu1 = WebDriverWait(driver, delay).until(EC.element_to_be_clickable((By.XPATH, "//a[#class='npsLogic_nodeViewEntry_c_/main_menu1_npsLogic']/img[#alt='-']")))
print("main_menu1 open! Close it first")
main_menu1.click()
print("closed!")
except TimeoutException:
print("main_menu1 was either already closed or something went wrong.")
driver.switch_to.parent_frame()
try:
iframe_cms_subpages = WebDriverWait(driver, delay).until(EC.element_to_be_clickable((By.XPATH, '/html/body/table/tbody/tr[2]/td[1]/iframe')))
print("iframe found!")
except TimeoutException:
print("Loading took too much time or the window was open.")
driver.switch_to.frame(iframe_cms_subpages)
try:
main_menu2 = WebDriverWait(driver, delay).until(EC.element_to_be_clickable((By.XPATH, "//a[#class='npsLogic_nodeViewEntry_e_/main_menu2_npsLogic']/img[#alt='+']")))
print("main_menu2 closed! Needs to be opened first")
main_menu2.click()
print("main_menu2 opened!")
except TimeoutException:
print("main_menu2 was either already open or something went wrong."
I use this line code to wait until product_id appear, My code works well for about 30 minutes
element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, product_id)))
And then, I get an error
TimeoutException(message, screen, stacktrace)selenium.common.exceptions.TimeoutException: Message:
I tried to change WebDriverWait to 100, but I still can't solve them. I want to understand why to appear this error and any solution for this case. Thanks so much !!!
This is my solution, but I want to know the cause of this error and looking for a better solution
while True:
driver.get(URL)
try:
element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, product_id)))
break
except TimeoutException:
driver.quit()
Can you provide HTML example? Without HTML example it's nearly impossible to debug for you. Code & HTML example will be helpful.
My guess is that your website uses dynamic id locators, or using iframe, or in your script, selenium is active on other tab/window/iframe/content. This is assuming your product_id is correct.
Update with the example:
from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
PRODUCT_ID = 'productTitle'
driver = webdriver.Chrome()
driver.get("https://www.amazon.com/dp/B08BB9RWXD")
try:
element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, PRODUCT_ID)))
print(element.text)
except TimeoutException:
print("Cannot find product title.")
driver.quit()
I don't have any problem using above code. You can extend it by youself :).
One thing that you need to note is that you DO NOT use while & break. WebDriverWait(driver, 10) already does the loop to find your element for 10 seconds. In this 10 seconds, if it finds your element, it will stop finding (break). You don't need to do the while loop yourself.
Hello I am new to python and I am trying to create an automation bot (I am very new to python) that logs into instagram and likes a certain number of posts but I am trying to figure out how to add a delay from when it enters the username information and password but I am not sure how to go about that, Also I would appreciate any feedback/recommendations thank you. Here is the code I have so far
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
browser = webdriver.Chrome()
def site_login():
browser.get('https://www.instagram.com/')
browser.find_element_by_name("username").send_keys(‘username’)
browser.find_element_by_name("password").send_keys(“password”)
browser.find_element_by_name("Log In").click() #not sure if this works
The time module should help you
import time
time.sleep(seconds) #Enter the time in seconds here
If you want to wait for a specified period then you can use time.sleep(timeInSeconds) which need import time.
import time
time.sleep(number_of_seconds)
But, however I would like to use the explicit wait. Unlike hard timeout, this will wait until the condition is met and continue the script.
# needed the imports
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
# wait for the element and click (using xpath locator)
WebDriverWait(browser, 10).until(EC.presence_of_element_located((By.XPATH, "xpath_goes_here"))).click()
# wait for the element and enter value (using css locator)
WebDriverWait(browser, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, "css_locator_goes_here")))).send_keys("enter input")
# store the element and then perform action
loginButton = WebDriverWait(browser, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, "css_locator_goes_here"))))
loginButton.click()
you can use either CSS or xpath location strategy.
You can also use implicit wait at the driver level by adding below line of code.
browser.implicitly_wait(10)
Instead of using time.sleep(seconds) , I would recommend to use Explicit wait as the previous comment. Since due to environment changes time can be vary. So its better to use Explicit wait. Using time.sleep(seconds) will sleeps the current thread. It's a bad practice.
Thanks
So recently tried to create a suite in python to run tests on some twitter share buttons. I used the "switch_to_frame" function to navigate into the iframe and select the button. Here is my code
class EntertainmentSocialMedia(CoreTest):
def testEntertainmentTwitter(self):
d = self.driver
d.get(config.host_url + '/testurl')
social_text = 'Follow #twitterhandle'
print "Locating Entertainment vertical social share button"
time.sleep(3)
d.switch_to_frame(d.find_element_by_css_selector('#twitter-widget-0'))
social_button = d.find_element_by_xpath('//*[#id="l"]').text
self.assertTrue(str(social_text) in str(social_button))
print social_button
d.close()
My concern is that with multiple tests in the suite, sometimes selenium will timeout. Is there something wrong with my code or can it be improved? Ideally I'd like them to be as robust as possible and avoid timeouts. Thanks!
Better wait for the frame explicitly:
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
...
d.get(config.host_url + '/testurl')
frame = WebDriverWait(d, 10).until(
EC.presence_of_element_located((By.ID, "twitter-widget-0"))
)
d.switch_to_frame(frame)
This would wait up to 10 seconds and then throw TimeoutException. By default, it would check the presence of frame every 500 ms.
Hope that helps.
I'm trying to automate processes on a webpage that loads frame by frame. I'm trying to set up a try-except loop which executes only after an element is confirmed present. This is the code I've set up:
from selenium.common.exceptions import NoSuchElementException
while True:
try:
link = driver.find_element_by_xpath(linkAddress)
except NoSuchElementException:
time.sleep(2)
The above code does not work, while the following naive approach does:
time.sleep(2)
link = driver.find_element_by_xpath(linkAddress)
Is there anything missing in the above try-except loop? I've tried various combinations, including using time.sleep() before try rather than after except.
Thanks
The answer on your specific question is:
from selenium.common.exceptions import NoSuchElementException
link = None
while not link:
try:
link = driver.find_element_by_xpath(linkAddress)
except NoSuchElementException:
time.sleep(2)
However, there is a better way to wait until element appears on a page: waits
Another way could be.
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
try:
element = WebDriverWait(driver, 2).until(
EC.presence_of_element_located((By.XPATH, linkAddress))
)
except TimeoutException as ex:
print ex.message
Inside the WebDriverWait call, put the driver variable and seconds to wait.