[Day #18 Pyats Series] Building test case hierarchy with Section, CommonSetup using pyATS for Cisco

[Day #18 Pyats Series] Building test case hierarchy with Section, CommonSetup using pyATS for Cisco [Python for Network Engineer]

Introduction on the Key Points

Welcome to Day 18 of the 101 Days of pyATS (Vendor-Agnostic) series!
Today, we’re stepping into a foundational topic that defines how you structure your test cases in pyATS using CommonSetup, CommonCleanup, and the powerful Section object. These aren’t just buzzwords—they are essential components that bring scalability and modularity to your automation scripts.

In traditional scripting, code is often written linearly, making it hard to reuse or conditionally execute logic. But pyATS, being a test framework built by Cisco, introduces object-oriented testing via AEtest—which is deeply powerful when used the right way.

If you’re learning Python for Network Engineer roles, mastering this structure will enable you to build test automation that’s reusable, maintainable, and production-ready.

By the end of this article, you’ll know:

  • What CommonSetup and CommonCleanup are
  • How Section helps in dynamically defining steps
  • How to build a complete test hierarchy for your Cisco infrastructure
  • How to reuse test logic across different devices and test plans

Topology Overview

To keep things simple, our testbed contains two Cisco IOS-XE routers connected to each other. You can simulate this setup in EVE-NG, GNS3, or Cisco DevNet Sandboxes.

Both devices are reachable via SSH, and the pyATS script will:

  • Perform device connectivity verification
  • Parse version info
  • Check interface status
  • Validate logical connectivity using ping

Topology & Communications

ComponentDetail
DevicesCisco IOS-XE Routers (R1, R2)
AccessSSH
Port22
AuthenticationUsername / Password
pyATS Features UsedAEtest, Section, CommonSetup, Testcase inheritance, Device connection/disconnection

Workflow Script: test_hierarchy.py

#!/usr/bin/env python3

from pyats import aetest
from genie.testbed import load

# Load the testbed
testbed = load('testbed.yml')

# Global device variables
R1 = testbed.devices['R1']
R2 = testbed.devices['R2']

# === TEST CASE STRUCTURE ===

class CommonSetup(aetest.CommonSetup):
    
    @aetest.subsection
    def connect_devices(self):
        R1.connect(log_stdout=False)
        R2.connect(log_stdout=False)

    @aetest.subsection
    def validate_connectivity(self):
        assert R1.connected, "R1 connection failed"
        assert R2.connected, "R2 connection failed"

# === ACTUAL TEST CASES ===

class VersionCheck(aetest.Testcase):

    @aetest.setup
    def setup(self):
        self.section1 = aetest.Section(parent=self, name='Check Version on R1')
        self.section2 = aetest.Section(parent=self, name='Check Version on R2')

    @aetest.test
    def run_section1(self):
        output = R1.parse('show version')
        self.section1.parameters['version'] = output['version']['version']
        print(f"R1 Version: {output['version']['version']}")

    @aetest.test
    def run_section2(self):
        output = R2.parse('show version')
        self.section2.parameters['version'] = output['version']['version']
        print(f"R2 Version: {output['version']['version']}")

class InterfaceCheck(aetest.Testcase):

    @aetest.test
    def check_interfaces(self):
        r1_intf = R1.parse('show ip interface brief')
        r2_intf = R2.parse('show ip interface brief')
        print("R1 Interfaces:")
        for intf, val in r1_intf['interface'].items():
            print(f"{intf}: {val['ip_address']} - {val['status']}")

        print("R2 Interfaces:")
        for intf, val in r2_intf['interface'].items():
            print(f"{intf}: {val['ip_address']} - {val['status']}")

class CommonCleanup(aetest.CommonCleanup):

    @aetest.subsection
    def disconnect_devices(self):
        R1.disconnect()
        R2.disconnect()

Explanation by Line

  • CommonSetup
    → Use it for shared setup activities like device connection. This is run before any testcases.
  • @aetest.subsection
    → Divides setup or cleanup into smaller logical blocks.
  • R1.connect()
    → Connects to the device using SSH. Output is suppressed for cleaner logs.
  • assert R1.connected
    → Validates the connection; if false, test halts immediately.
  • Section()
    → Creates nested logical sections within testcases. You can dynamically group steps, add logs, or use them for conditional test flows.
  • VersionCheck and InterfaceCheck
    → Independent testcases that demonstrate version parsing and interface validation.
  • CommonCleanup
    → Automatically disconnects the devices after test completion, ensuring clean state.

Sample testbed.yml

testbed:
  name: basic_lab
  credentials:
    default:
      username: admin
      password: cisco123
  devices:
    R1:
      os: iosxe
      type: router
      connections:
        cli:
          protocol: ssh
          ip: 192.168.100.10
    R2:
      os: iosxe
      type: router
      connections:
        cli:
          protocol: ssh
          ip: 192.168.100.11

Pro Tip: Make sure SSH is reachable and that your terminal or VM can ping both IPs before running pyATS scripts.


Post-validation CLI Screenshots

Sample Console Output:

Connecting to R1... Success
Connecting to R2... Success

R1 Version: 17.09.04
R2 Version: 16.12.05

R1 Interfaces:
Gig0/0: 192.168.1.1 - up
Gig0/1: unassigned - administratively down

R2 Interfaces:
Gig0/0: 192.168.1.2 - up
Gig0/1: unassigned - administratively down

You can export these logs as part of a report or collect them into a log file using logfile= in the connect() method.


FAQs

1. What is the purpose of CommonSetup in a pyATS test script?

CommonSetup is a reserved section used for initial configuration or setup tasks that need to be executed once before any test cases run.

For example:

  • Connecting to all devices from the testbed
  • Loading topology data
  • Initializing logging
  • Defining shared variables for the script

Think of it like a setUpClass() in unit testing frameworks, but specialized for network test automation.


2. How is CommonSetup different from Testcase.setUp()?

FeatureCommonSetupTestcase.setUp()
ScopeEntire script (global)Only for that specific Testcase
Execution OrderRuns before all test casesRuns before that test class
Use CaseConnect devices, define variablesPrepare preconditions for a test
ReusabilityAcross all test casesLocal to that test class

In short, CommonSetup is for global initialization, while setUp() is for test-specific prep.


3. What is a Section in pyATS and why do we use it inside CommonSetup?

Section allows you to organize multiple steps logically within a single block (like a method).
You can think of each @aetest.subsection as a micro-task (e.g., connect devices, verify reachability).

Sample structure:

class CommonSetup(aetest.CommonSetup):
    
    @aetest.subsection
    def connect_to_devices(self, testbed):
        testbed.connect(log_stdout=False)

Benefit:

  • Better structure
  • Independent failure detection
  • Easy debugging and log tracing

4. Can I reuse variables from CommonSetup in my test cases?

Yes. You can define script-level shared parameters using self.parent.parameters['key'] = value, which are accessible in test cases like:

self.parameters['key']

Example:

# In CommonSetup
self.parent.parameters['uut'] = testbed.devices['R1']

# In Testcase
device = self.parameters['uut']

This allows dynamic, data-driven testing across sections.


5. What happens if a subsection inside CommonSetup fails? Will other testcases still run?

By default, if a subsection in CommonSetup fails (e.g., device connection failure), the entire script can be marked as blocked or errored depending on the exception.

Best practice:

  • Catch exceptions inside subsection
  • Use self.skipped() or self.errored() to control flow
  • Handle critical failures with grace to avoid abrupt halts

6. Is it mandatory to use CommonSetup and Section in every pyATS script?

No, it’s not mandatory.

But if your script has:

  • Multiple devices
  • Dependencies
  • Shared logic

Then it’s highly recommended to use CommonSetup to avoid repetitive code. It leads to cleaner, modular, and scalable test scripts — especially when your framework grows.


7. Can I nest Sections or have multiple CommonSetup blocks?

You cannot nest Sections, and there can only be one CommonSetup class per script.
However, you can use multiple subsections inside CommonSetup, and invoke functions from other files to modularize logic.

If you need reusable logic, define it in helper modules and import them into your pyATS script.


8. How is the execution order determined between CommonSetup, Testcases, and Cleanup?

The pyATS framework has a fixed hierarchy and execution order:

  1. CommonSetup
    • subsection-1, subsection-2
  2. Testcase-1
    • setup()
    • test-section-1()
    • cleanup()
  3. Testcase-2
  4. CommonCleanup

Understanding this helps you design better automation logic, where dependencies are handled cleanly in setup and teardown stages.


YouTube Link

Watch the Complete Python for Network Engineer: Building test case hierarchy with Section, CommonSetup using pyATS for Cisco Lab Demo & Explanation on our channel:

Master Python Network Automation, Ansible, REST API & Cisco DevNet
Master Python Network Automation, Ansible, REST API & Cisco DevNet
Master Python Network Automation, Ansible, REST API & Cisco DevNet
Why Robot Framework for Network Automation?

Join Our Training

If today’s script made you think: “This is how enterprise automation should work!” — you’re right. And we have great news.

Trainer Sagar Dhawan is running a 3-Month Instructor-Led Course focused on:

  • Python Programming for Network Engineers
  • Ansible Automation
  • API Integration
  • Cisco DevNet-style workflows
  • Multi-vendor real-world testing with pyATS

Course Link:
View Course Outline & Enroll Now

This is the perfect next step if you want to build production-grade automation, apply for DevNet roles, or become an automation architect.

Learn Python for Network Engineer and join 1000+ engineers transforming their careers with NetworkJourney.

Enroll Now & Future‑Proof Your Career
Emailinfo@networkjourney.com
WhatsApp / Call: +91 97395 21088