In Playwright Java, both page.querySelector().getAttribute() and page.locator().getAttribute() can be used to retrieve element attributes, and they may return the same value.
What distinguishes querySelector() from locator(), and which one is recommended for reliable and maintainable tests?
page.querySelector() is similar to the browser’s DOM API, you get a single ElementHandle and have to manage waits yourself. page.locator(), on the other hand, returns a Locator object, which is smarter: it can retry actions, wait for visibility, and handle dynamic content.
I always recommend locator() if you want reliable interactions, like clicks or attribute checks, without manually adding waitForSelector().
From my experience, querySelector() is fine for quick grabs, but it doesn’t scale well for tests with dynamic content. locator() gives you a more expressive and maintainable API: you can chain assertions (expect(locator).toHaveText()) and it automatically retries until conditions are met. Using locators makes your tests less fragile and easier to read.
I used to use page.querySelector() a lot when I started with Playwright, but I ran into flaky tests. querySelector() only fetches the element once, and if the page updates or the element is recreated, it breaks.
Switching to page.locator() solved this because locators auto-wait and always reference the current element on the page.
For maintainable tests, I now prefer locator() for almost everything.