General Guidelines
Environment Requirements
Test suites, shall not depend on any pre-existing configuration of the environment (e.g. routers, switches), and shall always perform its own configurations, unless explicitly coded to be and/or provided/told otherwise.
Test suites shall always leave the environment (e.g. routers and switches) in the same state as they were found in (before the script started), regardless of its results and whether any errors occurred.
Test suites shall support both real and virtual (simulated) environment/hardware seamlessly.
Test suites shall be able to support a wide variety of execution environments, eg: Jenkins, docker containers, VMs, etc.
Headers & Comments
All files (modules) shall have headers using Python docstring notation, describing in detail its author, support contacts, purpose, description, usages & etc.
# Wrong import os import json import logging import importlib ...
# Correct #******************************************************************************* #* Test Script Template #* ---------------------------------------------------------------------------- #* ABOUT THIS TEMPLATE - Please read #* #* - Anything enclosed in <> must be replaced by the appropriate text for your #* application #* #* Author: #* <name>, Cisco Systems Inc. #* #* Support: #* <name>@cisco.com #* #* Read More: #* For the complete and up-to-date user guide on pyATS, visit: #* https://pyats.dev/ #* #******************************************************************************* import os import json import logging import importlib
All classes, functions, methods shall have headers using Python docstring notation, describing its purpose, usage, arguments, return values and providing examples.
# Wrong def extract_module(i, j, k): ... return find(i, j.expand(), k)
# Correct def configure_cdp(device, interfaces=None): """ Enables cdp on target device Args: device ('obj'): Device object interfaces ('list'): List of interfaces to configure cdp on Returns: None """
Code blocks shall be commented, describing its steps and purpose
# Wrong #### workaround Code #### s.send('conf t\r') s.expect('conf t.*#') s.send('hostname {}\r'.format(ctrl.custom.name)) s.expect('hostname.*#') s.send('end\r') s.expect('end.*#') s.send('wri mem\r') s.expect('wri mem.*#',timeout = 120) s.send('conf t\r') s.expect('conf t.*#') s.send('hostname {}\r'.format(ctrl.custom.name)) s.expect('hostname.*#') s.send('end\r') #########################
# Correct #### workaround Code #### # Send commands in altered order to set up environment # for proceeding tests. This code is a workaround for # host image memory not initializing properly device.hostname = device.custom.name device.configure('hostname {}'.format(device.custom.name)) device.execute('write memory', timeout=120) device.configure('hostname {}'.format(device.custom.name)) #########################
Convoluted logic shall be commented, including descriptions for each logic path.
# Wrong for device in route.hops(): net_freq = 0 if device[0] < 0 else max_freq - 1 if device[0] > max_freq - 1 else device[0] net_addr = 0 if device[1] < 0 else max_addr - 1 if device[1] > max_addr - 1 else device[1]
# Correct # Loop through devices and ensure network fabric # frequencies and addresses always fall within allowed values. # Reassign values if less than 0 or greater than max_freq/max_addr for device in route.hops(): net_freq = 0 if device[0] < 0 else max_freq - 1 if device[0] > max_freq - 1 else device[0] net_addr = 0 if device[1] < 0 else max_addr - 1 if device[1] > max_addr - 1 else device[1]
Code changes shall have in-line comments before the change, with the bug ID and a brief explanation of what’s changed.
Libraries & Packages
All users shall prioritize using and contributing to genie libraries. Uplifts should be made as needed.
Library & package requirements shall be clearly identified within the script header
Libraries & packages should be leveraging Genie abstraction concept/solution whenever possible
All import statements shall be explicit and shall occur at the top of the file.
# Wrong from x.y import *
# Correct from x.y import Z from . import x from .. import y
Traffic generation/control shall be done by using central, common functions and libraries.
All configurations shall be done/generated by calling functions/classes and providing them with corresponding parameter values. These functions/classes should belong to the common libraries.
All device output parsing (including screen scraping) shall be done using common library parsers.
device.parse('show version')
Errors & Exceptions
All exceptions and errors (including expected ones) shall be logged. Avoid silent exceptions
# Wrong try: log.info("Try to connect to console (connection a)") uut.connect(alias='con', via='a') except Exception as e: self.errored("Errored connecting to console. You're on your own.\n" + str(err))
# Correct try: log.info("Try to connect to console (connection a)") uut.connect(alias='con', via='a') except TimeoutError as te: log.error("Log relevant connection info here") self.errored("Connection timed out!\n" + str(te)) except InterruptedError as ie: log.error("Log relevant connection info here") self.errored("Connection interupted!\n" + str(ie)) except Exception as err: log.error("Log ALL connection info here") self.errored("Unexpected error!\n" + str(err))
Exception catching shall be explicit: never blanket catch all exceptions (
except:
statement without exception class type), or catching forBaseException
types.# Wrong try: some code except: some other code
# Correct try: some code except Exception: some other code
All code should prefer raising built-in exceptions whenever possible. Avoid creating excessive new exception types.
# Correct raise TypeError('...')
Test suite shall always test for both positive and negative logic paths.
# Wrong if api(): do something
# Correct if api(): do something else: do something else
Execution
Test suite shall be executable through job files (pyats run job execution).
Test suite shall leverage asynchronous (parallel) executions whenever possible.
Temporary file generation shall be done using python tempfile module, generated under the current runtime directory. All temporary files shall be deleted at the end of the run.
Test suite shall detect and report any anomalies during execution, such as crash, CPU freeze, memory leaks, etc. Look into pyATS Health Check.
Logging
Logging shall be done only through using python native logging module and functionality.
print()
function should never be used in Test suites and libraries.# Wrong print('This is some message')
# Correct log.info('This is some message')
Test suites must log thorough and informative messages describing its actions, purposes, progress and intermediate/final result.
# Wrong perform check 1 perform check 2
# Correct log.info('Performing check 1 to verify x is up') perform check 1 log.info('Performing check 2 to verify z is down') perform check 2 if all passed: log.info('All worked as expected') else: log.info('Failed because step <...> has failed') some logic
Point of failures and expected output/behavior/values shall be clearly identified in the log file.
# Wrong if dbgObj.verify_poe_deployment() == False: self.failed() else: self.passed()
# Correct if dbgObj.verify_poe_deployment() == False: # Give debug information ... log.error("Debug History:" + dbgObj.command_history + "\n \ Status: " + dbgObj.status) self.failed("Point of entry deployment verification failed.") else: self.passed()
Test results and any diagnostic information that may be helpful for debugging and bug-raising purposes shall be logged thoroughly.
Avoid using warnings excessively: in test automation, warnings are typically ignored.
Test suite should log a test topology diagram per test case if applicable.
Governance
Core infrastructure changes and feature requests shall follow the governance and priority matrix outlined in pyATS documentation.
Internally shared and/or externally open-sourced packages and libraries needs to have one or more identified owners. Each and every owner shall be responsible for their own project’s maintenance, and publishing guidelines in their repository README file.
Each test suite shall have an owner (individual or team), responsible of reviewing pull requests and changes to the test suite.