Using Variables across classes in Python

I am working on an open selenium project. I have two different test cases. In one test case I will be getting the value of customer_id_text which I need to use it in different test case. I have tried to make it as global variable but at that moment I am getting global variable undefined at module level. Also if I am executing the other test case it is executing the previous test case as well.

I want to use only that variable value. Below one is TC04 where I will be making customer_id_text as global

from unittest import TestCase
import time

from lib.ui.Login_page import LoginPage
from lib.ui.add_customer import add_customer
from lib.utils import create_driver

class TestAddCustomer(TestCase):
def setUp(self):
self.driver = create_driver.browser_instance()
self.login_page = LoginPage(self.driver)
self.add_customer = add_customer(self.driver)

def tearDown(self):
    self.driver.close()

def test_tc05(self):
    self.login_page.wait_login_page()
    self.login_page.username().send_keys('mngr280001')
    self.login_page.password().send_keys('uqytYgy')
    self.login_page.Login().click()
    self.add_customer.new_customer().click()
    self.add_customer.wait_for_new_customer_page()
    self.add_customer.customer_name().send_keys('sirendra')
    self.add_customer.gender_male().click()
    self.add_customer.dob().send_keys('05-11-2013')
    self.add_customer.address().send_keys('Jamnagar')
    self.add_customer.city().send_keys('Jamnagar')
    self.add_customer.state().send_keys('Gujarat')
    self.add_customer.pin().send_keys('567322')
    self.add_customer.mobile_no().send_keys('8000439025')
    self.add_customer.emailid().send_keys('Silverendra@gmail.com')
    self.add_customer.password().send_keys('67903')
    self.add_customer.submit().click()
    global customer_id_text
    customer_id_text = self.add_customer.customer_id().text
    success = self.add_customer.registered_successfully().text
    expected_result = 'Customer Registered Successfully!!!'
    print(customer_id_text)
    assert success == expected_result

    self.add_customer.continue_button().click()
    time.sleep(6)

Below is the code where I need to access customer_id_text variable value. But it is executing TC04 as well which I don’t want

from unittest import TestCase
import time

from lib.ui.Login_page import LoginPage
from lib.ui.add_account import Add_Account
from lib.utils import create_driver
from tests.regression.TC04 import TestAddCustomer

class TestAddAccount(TestCase):
def setUp(self):
self.driver = create_driver.browser_instance()
self.login_page = LoginPage(self.driver)
self.add_account = Add_Account(self.driver)

def tearDown(self):
    self.driver.close()

def test_tc05(self):
    self.login_page.wait_login_page()
    self.login_page.username().send_keys('mngr280000')
    self.login_page.password().send_keys('uqytYgy')
    self.login_page.Login().click()
    self.add_account.new_account().click()
    self.add_account.wait_new_account()
    cid_text = TestAddCustomer.customer_id_text
    self.add_account.customer_id().send_keys(cid_text)
    time.sleep(5)

When I am running only TC05 I am getting SyntaxError: name ‘customer_id_text’ is assigned to before global declaration

Please do help me how can I overcome this situation.

This is specifically not supposed to work: Each test is supposed to be independent. The unittest documentation on organizing test code puts it like this:

The testing code of a TestCase instance should be entirely self contained, such that it can be run either in isolation or in arbitrary combination with any number of other test cases.

Of course it makes sense that you’d need to set up a customer account and store its ID for use in a test. The unittest way of doing that would be to set up that account in the setUp() method of the test class, and store the ID in an object variable (e.g. self.customer_id_text) so the test methods in that class can use it.

Maybe you can extract the code that creates the customer ID into an independent function, so you can use it in TC04 as well as for setup in TC05?

Hi. Thanks for your response. But it may affect other test cases due to this setup. So is there any other way of working on this.

Thanks in Advance !!

No, none that would work reliably. If test cases can affect each other that’s the problem right there. If steps depend on each other they must either be part of the same test, or the preparation must be done separately for each test in a setUp() method.

If the reason you don’t want to do separate setups is that those customer accounts exist in some service that keeps running, consider starting a fresh instance of that service for each test, so there’s absolutely no interaction. I’ve done this using the Docker Python API: Start a container and wait for it to be ready in setUp(), and stop it in tearDown().

Hi. I have never worked on docker python. Why I have mentioned that it may create a problem is I don’t want to create separate setUp() and tearDown() for each and every test case.

Could you please share me some documentation over Docker Python so that I can work on this. I am stuck at this point and I don’t have much time as I need to complete it soon.

Also please do explain a bit on how to implement a docker for this specific scenario.

You’ll need to, but there are some ways to simplify it (which works better depends on your exact situation):

  • If you have a bunch of tests that need the exact same setup, you can create a subclass of TestCase, implement the shared setUp() and tearDown() there, and derive your final test classes from that, like so:
class AccountTest(unittest.TestCase):
    def setUp(self):
        # create the account for use in tests or something
        self.account_id = # ...
    def tearDown(self):
        # clean up the account

class MyTest(AccountTest):
    def test_account_stuff:
        # test things, you have access to the object variables created in setUp() here
  • If you just have common steps, you can put those into shared functions and call them in each test class’ setUp() as necessary.

Of course you can mix the two approaches, if needed. Your test classes can have their own setUp(), call a super().setUp(), and then customize.

I can’t tell you exactly how to do that because I don’t know your exact situation, it probably only makes sense if you either already have a Docker image for your service, or can create one relatively easily. The Docker Python API documentation is here. What I did was (slightly simplified) the following:

  1. Create a Docker image containing my service. I did that outside the test run, with a fixed label, but you could also do the build from Python.
  2. In setUp() start a fresh container from the image, get it’s IP address, and wait for the service to be ready. I pass detach=True and remove=True to the container run() function to run the container in the background and make sure it’s removed after being stopped (so the test cases don’t leave containers around). You can get the IP address (and a lot of other information) from the attrs property of the container object, you may have to call reload() on the container to update it.
  3. Run test against the service, accessing it directly by IP and port.
  4. In tearDown() stop the container.

I used the approach with a custom TestCase subclass to implement the Docker setup only once and use it for all tests.