But yes, when we tried to run with the patterns they advertise, the tests became indeed hard to write and read; maybe we did it all wrong but it's at least consistent with the experience expressed by you and others in the thread here.
Which is fine when the page object is a login form, but a bit more comfortable complicated when it's notification 5 or something.
get root() {
// It is important here to run cy.get()
// each time this property is accessed.
// Lots of methods of this class chain
// off of `this.root` so it needs to be
// re-evaluated every time.
return cy.get(rootComponentCss);
}
find(selector: string, options?: any) {
return this.root.find(selector, options);
}
...so that component-specific test helper objects (subclasses) can just do something like: get sendMissileAlertButton() {
this.root.find('.x-hawaii-missile-incoming');
}
That way, it looks up the target element every time it is used in a test.And in tests, you can just do:
tester.root.find('select.foo').select('hoge');
tester.sendMissileAlertButton.click();
EDIT: fixed code example, sorry! ;-)Most of the other open source tools (not to mention enterprise tools like Testim.io where I work) support the things that make Cypress "fast" like mocking network requests or automatically waiting for elements. I wrote an article about this somewhere I can dig it if you want.
If you have data showing differently I'd love to see it.
If you want to test the backend on its own, I personally would recommend using a different framework than cypress (or a different framework than the one being used for frontend e2e testing in general). Otherwise you risk that people conflate these two things and start "testing the backend" in e2e-tests for the frontend, which are super slow.
Cypress is a recent addition for us, so this is more a curiousity than anything else. I've done similar fully e2e / functional tests with puppeteer in the past.
I haven't had much time to try anything yet, but cypress's override of promises and async / await makes me think it'll be a pain to try adding non-cypress-specific checks to a cypress test.
You use Cypress to do something on the frontend, which triggers something on the backend and ideally that change is reflected on the frontend, you test that frontend change using Cypress.
For backend testing just directly call the backend API's and then check the database directly.
We run integration tests with Cypress and then rspec or some other Rails testing platform for the backend.
Cypress uses fixtures so ideally you would not let it hit a backend API unless you don't care about reproducibility of the response. For example a PUT will augment the data on the backend so when you run the test again the backend data will probably give you a different response to assert and fail your expected.
Cypress for the simple tests, and for the more complex case, e.g. opening tabs, testing your SSO login paths, I frame widgets, you end up using Playwright. I spend quite a lot of time to even fork Cypress to add support for testing WebKit browsers. Probably was quicker to write a Cypress kind of solution for Puppeteer/Playwright
And... I just couldn't figure out a way!
That said, I find checking for specific values in the page against other page values to be really difficult so if you're doing that with the table... good luck!
``` Cypress.Commands.add('yourHelperName'), () => { //your code } ```
Their examples show using Cypress commands for pedestrian things like "log in to app" but there is no need to get all Cypress-specific for things like that.
Instead, we typically write "test helper" objects for that, which are conceptually similar to the Page Objects pattern from the Selenium era. So like:
class GroupUiTester {
get groupChooser() {
return cy.get('#group-chooser-button');
}
}
Then, in your test, you can just be like: tester.groupChooser.click();
That is kind of simplified, but even that would be useful if you had dozens of tests interacting with that group chooser element. (Especially since, unlike string-based "cy.get('.some-class.whatever')" it will all auto-complete in any decent editor.)An example of "doing something weird" where we do use Cypress.Commands is like:
Cypress.Commands.add('disableDefaultLocalStorageClearing', () => {
Cypress['LocalStorage'].clear =
SUBSTITUTE_CYPRESS_CLEAR_LOCAL_STORAGE_FUNCTION;
});
We do that because the local storage clearing breaks a third party lib we use for a few tests. But that is something you can't just do with the regular Cypress/DOM API, so it is a good candidate for using Cypress "commands". x.usernameField.type('Joe User');
x.passwordField.type('$ecureBR0');
x.enterWrongCaptcha();
cy.get('.err-captcha-fail').should('be.visible');
Basically, I think all the code to locate UI elements and perform common interactions with them should be in re-usable utility code, so that the actual tests can focus on just testing what they test.I don't think the Cypress docs probably do a great job emphasizing how easy this is (especially since they go out of their way to de-emphasize the 'page objects' pattern, but without offering much in the way of a replacement).
Code that is just inspecting the DOM to find elements, or to do stuff to them that is just working up to the state where you can actually test something, should be in some shared object/library, but that is as easy as:
import { Whatever } from '/foo/bar';
Having that kind of code outside of the test probably makes the introductory "test hello world" example more complicated, but I think it's definitely a good practice in the real world, and isn't documented well.
The programming model is very odd. I found it next to impossible to share any code between tests, and most of the programming skills I've learned over the decades didn't seem to apply.
It is vastly superior to anything else we've seen in stability and polish. If I only could write reasonable code for it, things would be great...