Search code examples
pythonselenium-webdriverpython-unittesttest-suitemultiple-browsers

Python & Unittest open second browser, after first test have completed and test fails


Found many topics about this problem, but didn't find any easy described problem solving, so creating new topic with this problem and maybe will get much better answer to this problem.

I have created test case for LoginTest and test case for LegalPersonSearchTest. Both these test cases are in separate files login.py and legalPersonSearch.py

from utils.utils import *
from selenium import webdriver
from locators.locators import Locators
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
import unittest
from pages.loginPage import LoginPage


class LoginTest(unittest.TestCase):

@classmethod
def setUpClass(cls):
    cls.driver = webdriver.Firefox()
    cls.driver.implicitly_wait(10)
    cls.driver.maximize_window()
    cls.driver.get(URL)

def test_valid_login(self):
    driver = self.driver

    login = LoginPage(driver)
    login.fill_login_form(adminUsername, adminPassword)
    login.click_login_button()


if __name__ == '__main__':
    unittest.main()

I think the reason why new browser opens between tests, is declaring driver in second file.

from selenium import webdriver
import unittest
from pages.legalPersonSearchPage import LegalPersonSearchPage

class LegalPersonSearchTest(unittest.TestCase):

@classmethod
def setUpClass(cls):
    cls.driver = webdriver.Firefox()

def test_valid_organization_search(self):
    driver = self.driver
    legal = LegalPersonSearchPage(driver)

    legal.open_legal_person_search_page()

@classmethod
def tearDown(cls):
    cls.driver.quit()


if __name__ == '__main__':
    unittest.main()

How to modify LoginTest that browser open only one time, instead between tests?

I need to modify it, because after login (which is 1/2 of test) comes second part of test. It will complete and then browser closes and open new and start again with login.

As someone told me, then best practice is to keep login test part out of whole test.


Solution

  • To answer one of your questions -- driver gets opened twice because you have two different setUp() methods, one for each of your classes - LoginTest and LegalPersonSearchTest. When you run test_valid_login, your setUpClass() method gets called, which initializes the driver. Then, test runs. This driver instance never gets torn down, so window remains open.

    Then, test_valid_organization_search runs. This class ALSO has its own setUpClass() method, which initializes another driver instance. So now you have two drivers, both of which are opened in the setUpClass() method of each class file.

    This set up is producing results that you have not intended. I think you are slightly misunderstanding what "best practice is to keep login test part out of whole test" means here.

    This does not mean you need to write two separate tests -- one that logs in, and one that tests the rest of the functionality. This is actually bad practice, because you want your test cases to run independently of one another -- test cases should not rely on previous test cases to execute properly.

    I think the solution you are looking for here is to write the login functionality into your setUp() method so that login is always performed before a test case starts.

    This approach will prevent you from having the login part in with the test -- that's the original goal you mentioned here.

    You can adjust your classes by removing the duplicate setUp() method, and inheriting LoginTest into your LegalPersonSearchTest so that you are only setting up once:

    from utils.utils import *
    from selenium import webdriver
    from locators.locators import Locators
    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
    import unittest
    from pages.loginPage import LoginPage
    
    # this is our "base" class
    class LoginTest(unittest.TestCase):
    
    driver = None # driver instance that we will inherit into LegalPersonSearchTest
    
    # initialize driver, go to testing URL, etc.
    def setUp(cls):
        cls.driver = webdriver.Firefox()
        cls.driver.implicitly_wait(10)
        cls.driver.maximize_window()
        cls.driver.get(URL)
    
        # call login here to login before test
        login_page = LoginPage(cls.driver)
        login_page .fill_login_form(adminUsername, adminPassword)
        login_page .click_login_button()
    
    # move teardown into same class as setUp()
    def tearDown(cls):
        cls.driver.quit()
    
    

    We have declared a class variable for driver that you can pass into LegalPersonSearchTest. We have also removed the __main__ call, because the only entry point we need is the test_ method in LegalPersonSearchTest:

    from selenium import webdriver
    import unittest
    from pages.legalPersonSearchPage import LegalPersonSearchPage
    from login_test import LoginTest # unsure of file name containing LoginTest class
    
    class LegalPersonSearchTest(LoginTest): # inherit LoginTest here so we can use driver
    
    
    def test_valid_organization_search(self):
    
        legal_page = LegalPersonSearchPage(self.driver)
        legal_page .open_legal_person_search_page()
    
    
    if __name__ == '__main__':
        unittest.main()
    

    We have changed a few things here:

    • Entry point for main method designated by __main__ -- we only need this in one place, on the test case level (LegalPersonSearchTest)
    • Removed duplicate setUp() method calls that were resulting in multiple driver instances getting created (mentioned in your problem description)
    • Created single class instance of driver in LoginTest class to be passed into LegalPersonSearchTest class
    • Modified setUp() method on LoginTest to initialize the driver and login -- so that you do not have to include login code for any test!

    Update: Changes made following comments:

    • Removed login method from LoginTest class and moved login code under setUp() -- this will get performed before every test case now
    • Modified test_valid_organization_search to use self.driver instead of driver when initializing PageObject

    I've made many of these changes based on this sample Python test architecture that I pushed to my GitHub. The files I used as templates were base_test_fixture.py (for your LoginTest), and test_web_driver.py for your LegalPersonSearchTest.

    You may notice that base_test_fixture.py also contains def _testInitialize() and def _testCleanup() -- these are methods that you can override in your LegalPersonSearchTest class to perform additional steps to be run before your test_ method. The idea here is that if you have multiple .py files containing test cases, but each .py file requires slightly different setup, and you need to do more than just setUp() method calls for. You can override the testInitialize() and testCleanup() methods in the test case class, and these methods will execute after setUp() and before the actual test_ method.

    Let me know if you have any questions about this -- it's quite a bit to explain, but once you learn this architecture, you have a very powerful way of organizing and executing your test cases.