So in almost every project the Cypress tests are a procedural mess, while the Playwright tests are mostly well structured.
I know that Cypress has other patterns for dealing with this but they never seem to get applied.
await page.getStarted(); // what does this actually do?
vs await page.locator('a', { hasText: 'Get started' }).first().click();
await expect(page.locator('h1', { hasText: 'Installation' })).toBeVisible();
The second version is explicit and self-documenting. Tests don't always benefit from aggressive DRY, but I've seen teams adopt POMs to coordinate between SDETs and SWEs.--
Hadn't considered the Page Object Model and will definitely have to consider how to incorporate that for those who want to do things that way.
---
It was a few years ago, and very AngularJS focused, but I posted something along these lines: https://charemza.name/blog/posts/angularjs/e2e/consider-not-...
In summary: having thing look cleaner at a glance is not helping if you’re (almost) always going to need to do more than glancing
As usually, there is a balance to be found.
This argument also applies to using a function for abstraction.
I've just written a few dozen e2e tests with Playwright. The code looks like:
await invoiceDetailPage.selectCustomer(page, 'Acme');
await invoiceDetailPage.selectCustomerPoNumber(page, '1234567890');
await invoiceDetailPage.setCustomerReleaseNumber(page, '987654321');
...10-15 more lines
Each of those lines is 3 to 20 lines of Playwright code. Aggressive DRY is bad, but Page Object Models are usually worth it to reduce duplication and limit churn from UI changes. await page.getStartedLink.click()
expect(page.installationHeadline).toBeVisible()
Is much more readable, and on typing "page." you will see what props on your PO are available.Another note on your specific example: You are probably in the US and only have a single-language project. I am a Frontend Contractor in Europe and for the past 10 years didn't have a single project that was single language, hence the "hasText" selector would always be off-limits. Instead, very often we used the react-intl identificator as text content for code execution - which would make the above example look much more unreadable without POs, while with POs the code looks the same (just your PO looks different).
In an MVC conception, the UX model becomes a top layer of abstraction of the domain model. It's a natural place to be because for modern apps, users expect "more than forms", i.e.: different ways of cutting up the domain data, presented in different ways, ...
This is something that component-based frontend frameworks struggle with a bit: the hierarchical layout of the DOM doesn't always reflect the interrelations in data between parts of a user experience. Prop drilling is just a reflection of this fact and perhaps it's why we're seeing a rise in the use of state stores. It's not really about state, that's just the technical symptom, it's really about providing a way of defining a (in-browser) data model based on the user experience itself rather than the particularities of the UI substrate.
I worked at a place where a well meaning QA tech rewrote the test suite to use page objects. It was a total mess and I undid most of that work in the end. It just moved a bunch of xpath expressions a long way from the rest of the test code, and it was all single use anyway.
What to Submit On-Topic: Anything that good hackers would find interesting. That includes more than hacking and startups. If you had to reduce it to a sentence, the answer might be: anything that gratifies one's intellectual curiosity.
Off-Topic: Most stories about politics, or crime, or sports, or celebrities, unless they're evidence of some interesting new phenomenon. Videos of pratfalls or disasters, or cute animal pictures. If they'd cover it on TV news, it's probably off-topic.
This one seems reasonable.
I also find them very developer-centric — testers get forced into upfront design work that doesn’t fit how they naturally test, and many struggle with it. I’ve had better results by expressing behavior directly and keeping UI concerns thin, instead of using a wrapper around page structure.
However, the following design, thanks to Clojure's language design, helped address a rather nasty situation.
A tightly scoped Domain Specific Language, over some Types of PageObjects, all of which compose arbitrarily (without breaking value semantics). So, if you wanted the `value` of a Modal box having all sorts of switches, form fields etc., you'd call `value` on it, and it would call `value` on all of its constituents, and return the immutable hash-map snapshot of whatever state it found.
Cross-cutting concerns
| v PageObject / DSL -> | open | close | open? | select | deselect | ... |
|-----------------------+------+-------+-------+--------+----------+-----|
| Dropdown | | | | | | |
| Checkbox | | | | | | |
| Switch | | | | | | |
| Modal | | | | | | |
| SearchList | | | | | | |
| ... | | | | | | |
Concrete example (in the deck and demo linked below): (defprotocol IPageObject
"Each PageObject MUST implement the IPageObject protocol."
(page-object [this])
(exists? [this])
(visible? [this]))
And then an implementation like this: (defrecord Checkbox [target-css]
IPageObject
(page-object [this]
this)
(exists? [this]
;; webdriver check if target-css exists
)
(visible? [this]
;; webdriver check if target-css is visible
)
Selectable
(select [this]
;; webdriver select the target
)
(deselect [this]
;; webdriver undo selection
)
(selected? [this]
;; webdriver return true if target selected
)
Value
(get-value [this]
;; webdriver return current selection state
(selected? this)))
Deck: https://github.com/adityaathalye/slideware/blob/master/desig...Talk + Demo: https://www.youtube.com/watch?v=hwoLON80ZzA&list=PLG4-zNACPC...
I also commented about it here: https://news.ycombinator.com/item?id=45161410 (Clojure's solution to the Expression problem).
That said, UI testing is a hot mess in general (especially for SPAs). I prefer to avoid automating anything but the "happy paths". IME, exploratory testing is better at sussing out corner cases, and "emergent" misbehaviours. So I do that, in addition to the "happy path" suites. Cue: James Bach et. al. https://www.satisfice.com/
Also I am warming up to server-generated HTML, because I can unit-test that, if I can use `hiccup` syntax + HTMX. In that case, I just make all request handlers spit out HTML fragments as Clojure data, and test those responses in my test suite.
You might be asking too much of them to the point they become a burden.
1. Page Object Model should not contain any test-related stuff. It should abstract common page interactions:
registerUser({ email: string, password: string, displayName: string })
findProduct(name: string)
addProductToCart(id: string, quantity: number)
etcThen what you do in your tests is:
registerUser("abc@foo.net", "passw0rd", "foo name")
login("abc@foo.net", "passw0rd")'
// now you test
expect(page).findByText("Welcome back foo name").toBeVisible()
> and make even trivial UI changes ripple through dozens of files.2. Given my previous example, if you changed the login or registration form you'd only update the implementation of `registerUser`.
In any case I also want to emphasize: POMs are very useful when collecting and reusing the data (e.g. in the previous example `login()` would take no params and reuse those declared before), you can achieve 95% of their functionality with plain simple helper functions.
I'm sorry, but if your testers are not comfortable getting involved in the early design stages of your software in a 21st century world, then there's at least a 90% chance that their primary role at your company is perpetuating organizational dysfunction.
Most of my career has been defined by cleaning up the gargantuan messes the culture of "throw tickets over the wall to QA" created, and it has been very, very ugly. It defies common sense how culture around tools and processes for dev and ops roles continues to evolve over time, but for some reason testers are still trying to test software off in a silo, like it's released once or twice a year on CD-ROM.
But anyway if you were in that situation, duplication of selectors and labels and other implementation details of a page that were liable to change across a bunch of tests was an _absolute nightmare_ to deal with when you'd get a massive report of failures with bad error messages and often incorrect stack traces. Being able to fix all those errors by changing a selector in a single page object governing all of them was indeed better than the alternative.
In the era of Cucumber, page objects were yet another level of indirection around what was a far-too-indirect process to provide the kind of feedback loop you'd need for it to be valuable. Especially as part of a development workflow.
If a webpage is an iceberg, the buttons and menus and dropdowns are the visible part. If I understand this page object, that’s what he proposes testing.
But the bit I care about is the bit underneath that will compose REST calls from those UI elements and sometimes make subsequent REST calls from the result of the previous ones.
That is the tricky bit to test, and the bit where we *still* fall back to manual testing and recorded demos for qa.
I was hoping this was a suggestion for a better selenium.
Playwright also implements fixtures and Page Object Model:
a_t48•4mo ago
serial_dev•4mo ago
vasusen•4mo ago
JimDabell•4mo ago
I’m not certain that it buys enough to justify an extra layer of indirection in every front-end test though. Having a collection of selectors with meaningful names seems to get you about half the value without that extra layer.