Group Best Practices

When using a Group class in your test automation framework, following best practices can significantly enhance the readability, maintainability, and effectiveness of your test code. Here are key best practices to consider when writing Group objects:


1. Keep Group classes concise


A well-designed Group object should be concise and focused on a specific section or component. Avoid making the `Group class overly lengthy, as this can hinder readability and make maintenance more challenging.


Recommendations

  • Limit Lines of Code: Strive to keep the Group class manageable by limiting the number of lines. A concise `Group`` class is easier to read and understand.

  • Separate Concerns: If a Group class becomes too large, consider breaking it down into smaller, more focused classes or components if the framework supports it.


2. Group Element attributes


When a Group contains many elements (more than 8-10), it is advisable to group them logically. This helps in organizing the code and improves readability.


Recommendations

  • Logical Grouping: Group elements based on their functionality. For example, group footer controls, header controls, or form fields together.

  • Use Descriptive Names: Use clear and descriptive names for the groups and individual elements to enhance clarity.


Good practice

Here’s how you can organize a Group object with a large number of elements by grouping them logically:

from mops.base.group import Group
from mops.base.element import Element


class SeeMoreSection(Group):

    def __init__(self):
        super().__init__(...)

    # Footer Controls
    see_more_footer_logout_button = Element('#footer-logout', name='Footer Logout Button')
    see_more_footer_help_link = Element('#footer-help', name='Footer Help Link')
    see_more_footer_contact_us = Element('#footer-contact', name='Footer Contact Us')

    # Header Controls
    see_more_header_notifications_icon = Element('#header-notifications', name='Header Notifications Icon')
    see_more_header_user_profile = Element('#header-profile', name='Header User Profile')
    see_more_header_search_box = Element('#header-search', name='Header Search Box')

    # Main Content Controls
    see_more_main_content_title = Element('#main-title', name='Main Content Title')
    see_more_main_content_body = Element('#main-body', name='Main Content Body')

3. Reduce parent arg abusing


Note

This section describes a refactoring approach to reduce excessive usage of the parent argument within the Group class For detailed information, how parent arg work within Group class please refer to Group Key Features


The Problem

The parent argument allows you to specify a parent context from which the element’s location on the page will be determined. However, it should be used sparingly and only as an exception. And here is why…

The provided code demonstrates two ways to define elements within a SeeMoreSection class inheriting from Group. The “Bad example” relies heavily on the parent argument for elements within the “Footer Controls” section. This can lead to:

  • Reduced Readability: The code becomes cluttered with repeated usage of parent.

  • Maintainability Issues: Changes to the parent element might require modifying multiple child elements.

  • Deviation from the PCOM standard: The Page Component Object Model standard is no longer being met.


The Solution

The “Good example” showcases a better approach by defining a separate class named SeeMoreFooter that inherits from Group. This class encapsulates the footer elements, improving code organization and maintainability.


Benefits

By following this approach, you achieve:

  • Improved Code Readability: The code becomes more structured and easier to understand.

  • Enhanced Maintainability: Changes to parent elements or child elements are isolated within their respective classes.

  • Potential Code Reuse: The dedicated class can be reused in other parts of your codebase if applicable.


Bad practice

from mops.base.group import Group
from mops.base.element import Element


class SeeMoreSection(Group):

    def __init__(self):
        super().__init__(...)

    # Footer Controls
    see_more_footer_main_control = Element('#footer-main', name='footer main control')
    see_more_footer_logout_button = Element('#footer-logout', name='Footer Logout Button',
                                            parent=see_more_footer_main_control)
    see_more_footer_help_link = Element('#footer-help', name='Footer Help Link',
                                        parent=see_more_footer_main_control)
    see_more_footer_contact_us = Element('#footer-contact', name='Footer Contact Us',
                                         parent=see_more_footer_main_control)

    # Header Controls
    see_more_header_notifications_icon = Element('#header-notifications', name='Header Notifications Icon')
    see_more_header_user_profile = Element('#header-profile', name='Header User Profile')
    see_more_header_search_box = Element('#header-search', name='Header Search Box')

    # Main Content Controls
    see_more_main_content_title = Element('#main-title', name='Main Content Title')
    see_more_main_content_body = Element('#main-body', name='Main Content Body')

Good practice

from mops.base.group import Group
from mops.base.element import Element


class SeeMoreFooter(Group):

    def __init__(self):
        super().__init__('#footer-main', name='footer main control')

    see_more_footer_logout_button = Element('#footer-logout', name='Footer Logout Button')
    see_more_footer_help_link = Element('#footer-help', name='Footer Help Link')
    see_more_footer_contact_us = Element('#footer-contact', name='Footer Contact Us')


class SeeMoreSection(Group):

    def __init__(self):
        super().__init__(...)

    footer = SeeMoreFooter()

    # Header Controls
    see_more_header_notifications_icon = Element('#header-notifications', name='Header Notifications Icon')
    see_more_header_user_profile = Element('#header-profile', name='Header User Profile')
    see_more_header_search_box = Element('#header-search', name='Header Search Box')

    # Main Content Controls
    see_more_main_content_title = Element('#main-title', name='Main Content Title')
    see_more_main_content_body = Element('#main-body', name='Main Content Body')