How to use ChatGPT to polish up the code quality

jkrox

Jkrox

Posted on December 27, 2022

How to use ChatGPT to polish up the code quality

First off and foremost, everyone should be aware of the high impact trend that ChatGPT has had these weeks, if you live under a rock, it doesn't matter, you can check everything about ChatGPT here first, then come back here and continue reading 😁.

In my case, I'm going to use this in my field that is automation and testing using Selenium, however, this could be applied in any area, I'll emphasize on the way to ask questions to ChatGPT which is the aim for this post.

One of the ways to improve our code is to refactor it so that it is not redundant, alluding to the DRY principle. We can spend time trying new alternatives to follow this principle, however, if so we got some notions about this, either due to our experience and working on reading a lot of code from others just to make the code better, this tasks could tend to be tedious, that's why we use tools to avoid wasting time, in this case would be ChatGPT.

Here, we have this chunk of code:

def find_element(self, signer: str, value: str):
        LOCATOR_METHODS = {
            "XPATH": By.XPATH,
            "CLASS_NAME": By.CLASS_NAME,
            "CSS_SELECTOR": By.CSS_SELECTOR,
            "ID": By.ID,
            "LINK_TEXT": By.LINK_TEXT,
            "NAME": By.NAME,
            "PARTIAL_LINK_TEXT": By.PARTIAL_LINK_TEXT,
            "TAG_NAME": By.TAG_NAME,
        }

        locator_method = LOCATOR_METHODS.get(signer)
        if not locator_method:
            raise ValueError(f"Invalid signer: {signer}")

        return self.driver.find_element(locator_method, value)  

def find_elements(self, signer: str, value: str):
        LOCATOR_METHODS = {
            "XPATH": By.XPATH,
            "CLASS_NAME": By.CLASS_NAME,
            "CSS_SELECTOR": By.CSS_SELECTOR,
            "ID": By.ID,
            "LINK_TEXT": By.LINK_TEXT,
            "NAME": By.NAME,
            "PARTIAL_LINK_TEXT": By.PARTIAL_LINK_TEXT,
            "TAG_NAME": By.TAG_NAME,
        }

        locator_method = LOCATOR_METHODS.get(signer)
        if not locator_method:
            raise ValueError(f"Invalid signer: {signer}")

        return self.driver.find_elements(locator_method, value)
Enter fullscreen mode Exit fullscreen mode

In Selenium, we have these two functions which allow us to search one element (find_element) or more (find_elements) in the DOM, and I'm doing this because of a module where I type my own customised methods, so here we can see they are the same with the only difference that is, one is for singular and the other one is for plural. This can be improved using some techniques you're probably guessing now, as well as, we can implement our knowledge to get a better result with ChatGPT together, here we go!

We can start off by letting ChatGPT decide to give us an answer to this, just to see what we can expect from it. We leave a prompt like this:

Image description

As you can see, I put the code below (using shift + enter), this because sometimes when we provide a bad input way, it might give us a something wrong or we never wished.

ChatGPT's response was about suggestions but not an example in code, so we can type this respectively to get an answer based on the provided suggestions, here is:

Image description

And finally we got the response we wanted, code! But...

Image description

Yeah, the result is too long to put it as a screenshot here. So, here is completely:

class ElementFinder:
    LOCATOR_METHODS = {
        "XPATH": By.XPATH,
        "CLASS_NAME": By.CLASS_NAME,
        "CSS_SELECTOR": By.CSS_SELECTOR,
        "ID": By.ID,
        "LINK_TEXT": By.LINK_TEXT,
        "NAME": By.NAME,
        "PARTIAL_LINK_TEXT": By.PARTIAL_LINK_TEXT,
        "TAG_NAME": By.TAG_NAME,
    }

    def __init__(self, driver):
        self.driver = driver

    def find_element(self, signer: str, value: str) -> WebElement:
        """Find a single web element on the page.

        Args:
            signer: The type of locator to use (e.g. "XPATH", "ID", etc.).
            value: The value of the locator (e.g. the XPath expression or ID).

        Returns:
            The first web element matching the given locator.

        Raises:
            ValueError: If the signer is invalid.
            NoSuchElementException: If the element is not found.
        """
        locator_method = self.LOCATOR_METHODS.get(signer)
        if not locator_method:
            raise ValueError(f"Invalid signer: {signer}")

        return self.driver.find_element(locator_method, value)  

    def find_elements(self, signer: str, value: str) -> List[WebElement]:
        """Find multiple web elements on the page.

        Args:
            signer: The type of locator to use (e.g. "XPATH", "ID", etc.).
            value: The value of the locator (e.g. the XPath expression or ID).

        Returns:
            A list of web elements matching the given locator.

        Raises:
            ValueError: If the signer is invalid.
        """
        locator_method = self.LOCATOR_METHODS.get(signer)
        if not locator_method:
            raise ValueError(f"Invalid signer: {signer}")

        return self.driver.find_elements(locator_method, value)
Enter fullscreen mode Exit fullscreen mode

Although the code that ChatGPT is giving us is very good and could be perfectly usable, in my case it is not convenient for me in the context that I need it.

We can ask you to do the same, but ignore point 5 provided in the previous suggestions, because I don’t need it.

Point 5: If the code is intended to be used with multiple web drivers, you may want to consider abstracting the driver-specific code into a separate class or function, so that the main code can be easily adapted to work with different drivers.

Also, instead of using a class, do it in an auxiliary function or using decorators, with these ideas that I already had in mind at the beginning of writing this post, we can improve the answer, let's try to see what it gives us.

Image description

Again, the code is too long, so here is:

LOCATOR_METHODS = {
    "XPATH": By.XPATH,
    "CLASS_NAME": By.CLASS_NAME,
    "CSS_SELECTOR": By.CSS_SELECTOR,
    "ID": By.ID,
    "LINK_TEXT": By.LINK_TEXT,
    "NAME": By.NAME,
    "PARTIAL_LINK_TEXT": By.PARTIAL_LINK_TEXT,
    "TAG_NAME": By.TAG_NAME,
}

def find_element(driver: WebDriver, signer: str, value: str) -> WebElement:
    """Find a single web element on the page.

    Args:
        driver: The web driver to use.
        signer: The type of locator to use (e.g. "XPATH", "ID", etc.).
        value: The value of the locator (e.g. the XPath expression or ID).

    Returns:
        The first web element matching the given locator.

    Raises:
        ValueError: If the signer is invalid.
        NoSuchElementException: If the element is not found.
    """
    locator_method = LOCATOR_METHODS.get(signer)
    if not locator_method:
        raise ValueError(f"Invalid signer: {signer}")

    return driver.find_element(locator_method, value)  

def find_elements(driver: WebDriver, signer: str, value: str) -> List[WebElement]:
    """Find multiple web elements on the page.

    Args:
        driver: The web driver to use.
        signer: The type of locator to use (e.g. "XPATH", "ID", etc.).
        value: The value of the locator (e.g. the XPath expression or ID).

    Returns:
        A list of web elements matching the given locator.

    Raises:
        ValueError: If the signer is invalid.
    """
    locator_method = LOCATOR_METHODS.get(signer)
    if not locator_method:
        raise ValueError(f"Invalid signer: {signer}")

    return driver.find_elements(locator_method, value)
Enter fullscreen mode Exit fullscreen mode

However, this code would still be inconvenient, and this is because ChatGPT follows the thread of the conversation and follows what you type previously. For him to give us a totally different answer, and in which he does so to the ideas we have in mind, is simply to create a new chat.

Now let's try with this prompt:

Now, make me a function that doesn't make me repeat the same function twice to just change the find_elements to find_element. Here the code: The code is the same above at the beginning of the reading
Image description

Without further ado, the result here:

Here's a modified version of the code that combines the find_element and find_elements functions into a single function that takes an additional parameter return_multiple:

def find_elements(self, signer: str, value: str, return_multiple=False):
    LOCATOR_METHODS = {
        "XPATH": By.XPATH,
        "CLASS_NAME": By.CLASS_NAME,
        "CSS_SELECTOR": By.CSS_SELECTOR,
        "ID": By.ID,
        "LINK_TEXT": By.LINK_TEXT,
        "NAME": By.NAME,
        "PARTIAL_LINK_TEXT": By.PARTIAL_LINK_TEXT,
        "TAG_NAME": By.TAG_NAME,
    }
    locator_method = LOCATOR_METHODS.get(signer)
    if not locator_method:
        raise ValueError(f"Invalid signer: {signer}")
    if return_multiple:
        return self.driver.find_elements(locator_method, value)
    else:
        return self.driver.find_element(locator_method, value)

To use the function to find a single element, you can call it like this:

element = find_elements(self, "XPATH", "//div[@class='my-class']", return_multiple=False)

To use the function to find multiple elements, you can call it like this:

elements = find_elements(self, "XPATH", "//div[@class='my-class']", return_multiple=True)

Image description

This code is more convenient, it is shorter, but because it needs to add the docstrings, we are going to ask for it, as well as to use a decorator function. Here the input:

Now, make a version with decorator functions and add docstrings to it.
Image description

The result is too long, again 😐

Image description

Anyways, here is:

def element_finder(return_multiple=False):
    """Decorator function for finding elements.

    Parameters:
        return_multiple (bool): Whether to return a single element or a list of elements.
            If True, returns a list of elements. If False, returns a single element.

    Returns:
        element or elements: The element(s) found by the decorated function.
    """

    def decorator(func):

        def wrapper(self, signer: str, value: str):
            LOCATOR_METHODS = {
                "XPATH": By.XPATH,
                "CLASS_NAME": By.CLASS_NAME,
                "CSS_SELECTOR": By.CSS_SELECTOR,
                "ID": By.ID,
                "LINK_TEXT": By.LINK_TEXT,
                "NAME": By.NAME,
                "PARTIAL_LINK_TEXT": By.PARTIAL_LINK_TEXT,
                "TAG_NAME": By.TAG_NAME,
            }

            locator_method = LOCATOR_METHODS.get(signer)
            if not locator_method:
                raise ValueError(f"Invalid signer: {signer}")

            if return_multiple:
                return self.driver.find_elements(locator_method, value)
            else:
                return self.driver.find_element(locator_method, value)

        return wrapper

    return decorator


@element_finder(return_multiple=False)
def find_element(self, signer: str, value: str):
    """Find a single element on the page.

    Parameters:
        signer (str): The method to use for finding the element.
            Can be one of the following: "XPATH", "CLASS_NAME", "CSS_SELECTOR", "ID",
            "LINK_TEXT", "NAME", "PARTIAL_LINK_TEXT", "TAG_NAME".
        value (str): The value to use with the specified locator method.

    Returns:
        element: The element found on the page.
    """
    pass


@element_finder(return_multiple=True)
def find_elements(self, signer: str, value: str):
    """Find multiple elements on the page.

    Parameters:
        signer (str): The method to use for finding the element.
            Can be one of the following: "XPATH", "CLASS_NAME", "CSS_SELECTOR", "ID",
            "LINK_TEXT", "NAME", "PARTIAL_LINK_TEXT", "TAG_NAME".
        value (str): The value to use with the specified locator method.

    Returns:
        elements: The elements found on the page.
    """
    pass

element = find_element(self, "XPATH", "//div[@class='my-class']")
elements = find_elements(self, "XPATH", "//div[@class='my-class']")

Enter fullscreen mode Exit fullscreen mode

In conclusion, we can see that in these ways we can polish up our code with the help of ChatGPT, we can keep in mind these certain things:

  • If we want that ChatGPT generates totally different answers, we can generate a new chat and give an input with brief changes.

  • It gives us very good implementations by itself, by not giving it so much context as general answers, if we put more specific things in it, it improves, thanks to the fact that we put together what we could have implemented manually ourselves, but ChatGPT interprets it well, and we already value the result at the end.

Thanks for stopping by, I hope you find these ways to use ChatGPT useful, let your imagination run wild :D

πŸ’– πŸ’ͺ πŸ™… 🚩
jkrox
Jkrox

Posted on December 27, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related