Reading:
Elevating Your Test Automation Projects With Meaningful Code Documentation

Elevating Your Test Automation Projects With Meaningful Code Documentation

Communicate what your automation does with meaningful documentation

It’s inevitable: sometimes you have to read existing code for automated tests to figure out what it is they are supposed to accomplish. What’s more, the process of doing so can be a very time-consuming and frustrating task. If you already are busy with high priority tasks like regression testing, you can be left wondering if it is a good use of time to try to unravel the seeming spaghetti in the bowl. Or, you think, maybe you should push through: after all, test maintenance and understanding what test suites do are a part of your job.

As a newbie or even a not-so-newbie on an existing test automation project, you can be left baffled looking at someone else‚Äôs code. Sometimes you even go back to your own code you wrote a while back, only to be left thinking ‚ÄúI‚Äôm not sure what is going on here.‚ÄĚ Common causes of confusion are:

  • The names of methods, functions, and classes do not follow a clear naming convention and are not very informative about their purposes.
  • There is little or no documentation in the form of documentation strings (docstrings) or comments that could provide definitions for functions and classes.

The Importance Of Meaningful Documentation In The Codebase

On most projects, you are working with other test engineers. Having good documentation can make that collaboration much smoother. In this article, you will see how using comments, docstrings, and a naming convention in your code will make your automation project more maintainable and understandable for you and everyone else on the team. By documenting your code responsibly, you will make it less painful for others to reuse your code when making contributions to the project. Your coworkers (and managers) will appreciate the effort.

Examples will be provided in JavaScript. Not to worry though: these tips are transferable to other programming languages, provided that you use the correct documentation syntax as you move from one language to another.

Meaningful Code Comments

Comments are annotations in the codebase. They take the form of text strings, and in JavaScript they are preceded by two forward slashes, //. They are a simple and effective way to add context to the code. Typically these are added to explain how your code works and what the intention was in writing it. 

Two Easy Rules For Comments 

The purpose of the comments is to inform the reader in phrases or sentences that are concise, thereby increasing the readability of the source code. When thinking of writing comments, you should first consider whether it is even needed at all. The following rules can be used as a guide.

  1. Avoid redundancy and undue repetition. If the reader can decipher what the code is about from the code itself, you don’t have to include a comment. 

Let’s say you are testing the sign-in workflow with a framework like Cypress that by default allows for good test organization and test descriptions. Using the test below as an example, it would be redundant to sprinkle more comments throughout the test. Since there is already a description that says you are expecting a successful login, you can remove all comments and expect that a test written like this will still be clear and readable. 

// login.cy.js

describe('Should test the sign in function', () => {
  it('should sign in successfully', () => {
    // Navigate to the website
    cy.visit('the-testwebsite.com')

    // Enter the username
    cy.get('#username').type('user09876')

    // Enter the password and press the Enter key
    cy.get('#password').type('testpassword{enter}')

    // Verify that the URL is now the inventory page URL
    cy.url().should('include', '/inventory.html')
  });
});

  1. Be informative and concise. If the implementation is complicated and easily misunderstood, you should add a code comment to explain the purpose. Generally, when you add your own classes and functions to the project, comments are in order.

Often in test automation, more complex lines of code are tucked away out of sight as part of classes in page object files and other supporting files. You can see an example of this practice below in the getRandomState function. This function was designed to do some action on data being used in the test suite. The function’s author added comments that defines what the function does and also lets the reader know that some data was imported to make this work. 

// dataHandler.js

const listOfStates = require("../data/states");
export class DataHandler {
  // Returns a random state from a list of states
  // Note: listOfStates is a list imported at the top of the file
  getRandomState() {
    const numberOfStates = listOfStates.length
    const randomStateIndex 
      = Math.floor(Math.random() * numberOfStates) + 1;
    const state = listOfStates[randomStateIndex]
    return state
  };
};

The Power Of Docstrings

Another useful way to document code is by adding docstrings. Docstrings are like comments and are associated with a specified block of code, giving the user information about what that object is and how to use it. 

Docstrings are really helpful because of how well they increase the reusability of code. With these in place, you can see the class and function definitions that were added. When using text editors like Visual Studio Code, you can also hover over the objects to see the contents of the docstrings, making it even easier to use.

Screenshot of code from visual code. A javascript function called signUp is highlighted and shows a popup which shows the docstring for the signup function. The docstring contains a description of the function "Fills out sign up form and submits it." and descriptions for parameters. For example "@param username - the user's username".  

A screenshot of javascript code in visual studio. A module called SignupPage is highlighted showing a docstring that describes what the module does. The description states "Stores methods and element locators for the sign-up page"

Docstrings are particularly helpful when using the page object model or other helper files to develop your automation project. It is expected that you and your team members would want to reuse the objects in these files, and docstrings facilitate that. They provide an explanation of what the object does to people who might want to reuse your code. The functions and classes within these files are great candidates for using docstrings.

In JavaScript, docstrings are added to the lines above the declared object. For other languages like Python, they are typically added as the first statement within the code block after the object is declared.

The first line of the docstring is typically a short description of what the object does. Subsequent lines would list different types of information associated with the object. Types of information that are often documented include: 

  • Function arguments using the¬†@param tag. The data type of each argument should be indicated in brackets - {}.¬†
  • Examples of how to use functions using the¬†@example tag. Seeing examples of exactly how to use a function can be very helpful to collaborators looking to reuse your code.¬†
  • A return value using the¬†@returns tag. Though not always a part of the code block, stating the return value is encouraged.

The example below shows the SignupPage class with a short definition of what this class does. Meanwhile, the signUp method has its own definition, showing its parameters and an example of how to use it.

// signupPage.js
/**
* Stores methods and page elements for the Sign-up page.
*/
export class SignupPage {
  /**
  * Fills out the Sign Up form and submits it. 
  * @param {*} username the user's username
  * @param {string} password the user's  password
  * @param {number} age the user's age
  * @example signUp('testuser', 'testPassword', 19)
  */
  signUp(username, password, age) {
    cy.get('#user-name').type(username)
    cy.get('#password').type(password)
    cy.get('#user-age').type(age)
    cy.get('#signup-button').click()
  };
};

Developing And Following Good Naming Conventions

A naming convention is a set of standards for naming variables, functions, classes, files, directories, and so on. These standards, while they are not hard and fast rules, are widely accepted by most developers. 

Naming conventions should be used because they make it easier to keep data organized and consistent throughout your project, making it easier to understand your code. Setting this standard for projects makes projects more cohesive.

Types Of Naming Conventions

Four of the most popular conventions are:

  • Camel Case, where the first letter is lowercase. If there are multiple words in the name, subsequent words should begin with an uppercase with no spaces between words. Examples are a variable called¬†firstName, a function named¬†getRandomState, and a file called¬†userData.js.
  • Snake Case, which has all lowercase letters with each word in the name separated by the underscore (_). For example,¬†first_name,¬†get_random_state,¬†user_data.js.
  • Kebab Case, like Snake Case, has all lowercase letters but uses the hyphen (-) instead of the underscore. Examples are¬†first-name,¬†get-random-state,¬†user-data.js.
  • Pascal Case, where all words in the name start with an uppercase letter. As in Camel Case there is no space between words. Examples are:¬†FirstName,¬†GetRandomState,¬†UserData.js.

With naming conventions, it is important to use descriptive names, avoiding abbreviations and acronyms that are not widely known or will be difficult to remember. This strategy is helpful in informing the reader what the object in question is and increases the readability of your source code. For example, if you have a variable representing the hourly rate of an employee, and you call it ehr, this could be misinterpreted by the reader. However, using a name like employeeHourlyRate lets the reader know exactly what the variable represents. 

You should use a similar line of thinking when considering file names. If you have a page object file representing the ‚ÄúMy Information‚ÄĚ page of an application, a name like¬†myInformationPage.js would provide just enough description to the reader.

In creating names, it is customary to prefix the name with a noun or verb depending on the type of element being named. The names of functions and methods should begin with a verb, since these represent an action to be done. For example, a method that gets a random state from a list of states might be prefixed by the verb get and be called getRandomState. 

Other elements, such as classes, variables, and file names that represent some data or entity, would typically begin with a noun. For example a file that holds tests for valid user login test cases could be named validUserLogin.spec.js, while a variable that represents a valid user password to be used in a test case could be named validPassword. Names that begin with verbs can be useful as well, especially if the variable contains elements that perform an action. For example, a variable that stores the page element of a button that sends an email could be named sendEmailButton. In such a case, the name is prefixed by the verb send but is still in accordance with the guidelines of the convention, since this variable name is descriptive of the element that it represents.

Choosing A Naming Convention

In deciding what naming convention to use, it is a safe bet to go for the conventions that are already widely used within the programming language of your source code. You can also decide with your team what standards you want to use, making sure to be consistent with them once you’ve adopted a convention. In JavaScript, for example, it is common to use Camel Case for variable names and file names. However, when it comes to class names, the Pascal Case is the convention that is used. 

Conclusion

The goal of effective code documentation strategies is to provide information that increases the readability and reusability of your source code. Ask the question ‚ÄúWould I understand this code if I didn‚Äôt write it?‚ÄĚ If the answer inclines toward a ‚Äúno‚ÄĚ, make sure to add comments, but only where necessary, and add docstrings to your methods and classes.¬†

When you create names for files and the variables within them, stick to a naming convention. Also be sure to be descriptive with names so that they are not easily misinterpreted. Putting these tips into use will undoubtedly elevate your test automation project and improve your collaboration efforts.

For More Information

Yanique Dickson's profile
Yanique Dickson
Comments
Explore MoT
Managing Distributed QA Teams: Strategies for Success
In an era where remote teams have become the norm, mastering the art of managing hybrid and distributed QA teams is more crucial than ever
MoT Foundation Certificate in Test Automation
Unlock the essential skills to transition into Test Automation through interactive, community-driven learning, backed by industry expertise
This Week in Testing
Debrief the week in Testing via a community radio show hosted by Simon Tomes and members of the community