From my previous experiences with automation, I'm usually the only automation tester on a project, so I'd write test specs with function names that were vague and not meaningful, but I knew what they did. This left the spec files only readable for myself or people who made the valiant effort to go through the code and understand the function.
This isn't an issue when I'm the primary user of the framework, but as the team grows, it becomes more difficult for new QAs to understand. Being on a project that has more than myself as the Automation QA, I understood the pain it took people to understand how my test suites worked before being able to add their own. After some discussion and feedback from my QA colleague, we came to an agreement which I have grown quite fond of, it not only simplifies reading the spec and understanding what it does but also has a clear understanding of why we wrote those test cases.
This is how my automation test cases looked when I first started (albeit slightly better) and not much changed until recently.
typescriptimport {fixtures} from "../fixtures";import {SwagLabsPage} from "../pages/SwagLabsPage";import {LoginPage} from "../pages/LoginPage";const { it, describe, beforeEach } = fixtures;let swagLabsPage;let loginPage;describe("SauceLabs UI", () => {beforeEach(async ({ page }) => {swagLabsPage = new SwagLabsPage(page);loginPage = new LoginPage(page);await page.goto("/");});it("Login", async () => {//Logged inawait loginPage.isElementVisible(loginPage.loginButton)await loginPage.setText(loginPage.usernameInput, "standard_user");await loginPage.setText(loginPage.passwordInput, "secret_sauce");await loginPage.page.click(loginPage.loginButton);//Validate user logged in successfullyawait swagLabsPage.isElementVisible(swagLabsPage.title)await swagLabsPage.isElementVisible(swagLabsPage.burgerMenu)await swagLabsPage.isElementVisible(swagLabsPage.inventory)});});
Are you able to understand what the test does without the grey comment lines? This was not something easily understandable, even from a developer's perspective.
The agreed way my colleague and I came to after some discussion on how the test cases can be improved. (Thanks Sandeep!)
typescriptimport {fixtures} from "../fixtures";import {SwagLabsPage} from "../pages/SwagLabsPage";import {LoginPage} from "../pages/LoginPage";const { it, describe, beforeEach } = fixtures;let swagLabsPage;let loginPage;describe("SauceLabs UI", () => {beforeEach(async ({ page }) => {swagLabsPage = new SwagLabsPage(page);loginPage = new LoginPage(page);await page.goto("/");});it("Verify user able to login and view inventory page @smoke", async () => {/*** QA-1* Scenario : As a consumer,* I want to be able to log into the website,* So that I can browse the products available*/await loginPage.login();await swagLabsPage.assertUserLandsOnInventoryPage();});});
I feel it's much nicer with meaningful test names and a description with the correlating ticket number and the user story. In my perspective, this adds much more value, more easy to understand what is being tested, and where the requirement comes from. Going forward, I will be creating test cases like this from the beginning.
This is easily achievable by moving all the actions and expectations into functions with meaningful names.
typescriptimport { Page } from "playwright";import {BasePage} from "./BasePage";export class LoginPage extends BasePage {usernameInput = `input[data-test="username"]`passwordInput = `input[data-test="password"]`loginButton = `input[data-test="login-button"]`constructor(page: Page) {super(page);this.page = page;}async login(){this.isElementVisible(this.loginButton)this.setText(this.usernameInput, "standard_user");this.setText(this.passwordInput, "secret_sauce");this.page.click(this.loginButton);}}
typescriptimport { Page } from "playwright";import { BasePage } from "./BasePage";export class SwagLabsPage extends BasePage {title = `span[class="title"]`burgerMenu = `button[id="react-burger-menu-btn"]`inventory = `div[id="inventory_container"]`constructor(page: Page) {super(page);this.page = page;}async assertUserLandsOnInventoryPage(){await this.isElementVisible(this.title)await this.isElementVisible(this.burgerMenu)await this.isElementVisible(this.inventory)}}
Not a lot of change or rework is required to achieve this cleaner state.
What are your thoughts on the two versions, and which would you prefer? Do you see any improvements that you would make, or anything that isn't useful that should be removed?