Below I composed a list of tips and gotchas from more then 5 years of experience in test automation, Clean Code and PEP8. The list is not structured and contains both Python specific advices as well as general ideas. All the points only represent author’s humble opinion and should be taken at your own rist. Please note that this is not AI generated articleπ although ChatGPT helped with HTML formatting and proofreading.
Variables
Local Variables
Local variables should have descriptive names in lower_snake_case. Use explicit words whenever possible. Avoid using abbreviations or mind mapping, except for extremely common terms like x (x-coordinate), or env (environment).
# β Poor
num = 0
current_t = 0
# β
Better
number_of_retries = 0
current_time = 0
Constants
Python does not have built-in constants. To signal that a variable should be treated as constant (read-only), use UPPER_SNAKE_CASE.
# β
Constant convention
TIMEOUT = 60
Underscore
Underscores can be used to avoid conflicts with reserved keywords or other variables. A single underscore ( _ ) is also a common convention to indicate a throwaway value.
# β
Throwaway variable in loop
for _ in range(10):
some_action_to_repeat()
Functions
Functions should have descriptive names that convey their purpose without needing to read the code or documentation. Type hinting is strongly encouraged.
# β Poor
def f(): ...
def search(s): ...
# β
Better
def find_user_by_name(name: str) -> Optional[User]: ...
Boolean Return Values
Function names returning boolean should start with is_.
# β Poor
def element_present(): ...
# β
Better
def is_element_present(): ...
Private Functions
Prefix function names with an underscore to indicate they are intended for internal use only.
# β Poor
def this_function_private(): ...
# β
Better
def _this_function_private(): ...
Assignments
Tuple Assignment
Use tuple unpacking for functions returning multiple values.
# β Poor
dimensions = retrieve_dimensions()
length = dimensions[0]
width = dimensions[1]
height = dimensions[2]
# β
Better
length, width, height = retrieve_dimensions()
Conditional Operators
Ternary Operator
Python supports conditional expressions for inline ternary logic:
# β
Preferred
result = value1 if condition else value2
# π Acceptable but less safe (can be buggy in some cases)
result = value1 and condition or value2
Nested Ifs
Try to avoid deep nesting. Use guard clauses instead.
# β Poor
if condition1:
if condition2:
if condition3:
...
# β
Better
if not condition1:
return
Simplifying Boolean Returns
# β Poor
if condition:
return True
return False
# β
Better
return condition
Avoid Negative Clauses in Function Names
When naming functions that return booleans, prefer positive phrasing. This improves readability and avoids the cognitive overhead of double negatives.
# β Poor
if is_not_user_logged_in():
# β
Better
if not is_user_logged_in():
Loops
Comprehensions
Use list comprehensions when appropriate for clarity and conciseness.
Timeout Loop
# β Poor
while tries < timeout:
# condition to exit
time.sleep(1)
tries += 1
# β
Better
start = datetime.now()
while (datetime.now() - start).seconds < timeout:
# condition to exit
time.sleep(1)
Functions: Developer Mindset
While unit testing for automation code may not be enforced (comparing to production code), writing testable code and thinking in terms of testability can improve maintainability and quality.
Function Should Do One Thing
Watch out for red flags:
- Long functions
- Complex logic
- Repeated logic
Try to Limit Parameters
Fewer parameters usually lead to simpler, easier to test and more reusable functions.
Returning Boolean
# β Poor
def some_function(self):
if is_element_present() and is_element_enabled():
return True
else:
return False
# β
Better
def some_function(self):
return is_element_present() and is_element_enabled()
Comments
Make sure to include comments when necessary, however keep in mind that clean self-descriptive code does not require comments.
General Advices
Reinventing the Wheel
Avoid reinventing existing functionality. If you think what you’re creating may already exist in Python built-in, 3rd party or internal libraries search for ready made solution first.
Types
Type Checking
# β Poor
if type(variable) == list:
# β
Better
if isinstance(variable, list):
Why:
class A: pass
class B: pass
b = B()
type(b) == A # False
isinstance(b, A) # True
Comparing with Singletons
# β Poor
if x == None:
if x == True:
if x == False:
# β
Better
if x is None:
if x is True:
if x:
if x is False:
if not x:
Data Structures
Comprehensions
Use comprehensions for short, readable logic.
# β Poor
double_array = []
for i in array:
double_array.append(i**2)
# β
Better
double_array = [i**2 for i in array]
No Need for Indexes
# β Poor
for i in range(len(array)):
print(array[i])
# β
Better
for element in array:
print(element)
# β
If you do need the index
for i, element in enumerate(array):
print(f"index={i} element={element}")
Conclusion
Keep these tips in mind next time you are writing automated tests, to keep them clean, maintainable and robust. As with all ‘best’ practices, take them with a grain of salt. Rules are created to violate them, so next time when reality hits and you need to create function with 15 parameters, think about it twice and do ya thing.
I’m bad, and that’s good. I will never be good, and that’s not bad. There’s no one I’d rather be than me.
-Ralph Wreck-it-Ralph
I am a former aerospace engineer turned web development enthusiast with a passion for exploring the digital landscape. My interests include SEO, automation, and web scraping, where I enjoy finding innovative ways to optimize and enhance online experiences. Beyond the world of technology, I have a love for finance, chess, and playing the piano. In my free time, you can find me swimming, hiking, or diving into the latest video games. Always eager to learn and grow, I enjoy blending my technical skills with my diverse hobbies to keep life exciting and fulfilling.