Hacker News new | ask | show | jobs
by wilsonrocks 2022 days ago
The idea behind the code being async is that you don't know how long things will take, especially talking to a backend, or even sometimes DOM manipulations in dynamic pages like you mention.

Having said that, I find parts of it v confusing (although not the asyn stuff) especially around assertions - there seem to be multiple ways to do things and I'm never clear which is which.

As a sibling reply said, try to write cypress (or other similar product) tests not to rely too much on layout - I use html data attrivutes rather than text or Dom based selectors, so the tests can survive most redesigns, and if they don't, it's because the user journey has changed enough that we _should_ be changing the tests.

1 comments

There are much simpler programming models to deal with long-running actions, like processes and threads. The essential point of async is to be thread-like, but with lower overhead; or in environments where you cannot use processes and threads - but there's no very convincing reason for testing to be it. And choosing to prioritize dealing with runtime overhead over dealing with coding complexity isn't an obvious win. Note that promises make sense for processes and threads too; the key question is whether you really block, or virtually block.

Nothing wrong with async code; but it's often sold as the "obvious" solution to, well, several things happening and some of them taking long - and it's just not that great a solution.

Would threads or processes be a great way of simulating how a user interacts with a web app though?

I tend to think of them clicking things and waiting for a response then moving on to the next thing when that happens (or failing the test when it doesn't) which seems to fit well with an async model.

Cypress _does_ do a lot of magic behind the scenes to stop you explicitly using async/await or promises in the code... Which occasionally bites it.

Especially when you have to use a .then to use values you pull from the Dom.

You can wait in a threaded model too, right? The only difference is that if you do so, you're blocking that thread. The pain comes when you mix the two models, because async often deals really poorly with blocking. In general, the differences between the two models when used cleanly are quite small. When most waits are no-ops, threads have significantly lower overhead; but when waits are common, especially if long-lived, async overhead is much lower.

In fact, if you want to minimize the chance of deadlocks, using threads or processes for every action is safest; although of course that means you may get issues with concurrent modification. But though that risk is serious, it's worth noting that async code doesn't actually solve it; after all, every await may mean a context switch, and thus if you have shared mutable state living across an await, all bets are still off. async-only code with exactly 1 thread only provides safety to the extent that modification is encapsulated and that the module within which the internal datastructures are updated does not use async. And that's not nothing; it's kind of as if a java object was synchronized on all methods. But it's also not reliable enough that you can just forget about concurrent modification, either.

But if all you want to do is automate a browser, those overheads are completely irrelevant, and this story about concurrent modification likely is too. You could use either model equally well. To be explicit: there's nothing about promises that means you need to use async. It's only once you start chaining a bunch of them that you enter async territory, but why would you ever do that in a threaded model when automating a browser? The browser is so heavy weight, just fork a bunch of processes already, and KISS: no need to worry about any of this, at all.