I am using selenium framework to automate a web application wherein basically, driver.findElement() is commonly used. but I got the suggestion that #FindBy() works faster than driver.findElement(). suggest me which is better to use ?
When the code - PageFactory.initElements(....) is run it creates Proxy objects for all the fields which are annotated with #FindBy (or even #FindBys and #FindAll). So initially no searching of WebElements are performed.
Then if something like element.sendKeys(...) is run, the actual WebElement is searched using driver.findElement(...) before sendKeys() is run. Then if the same sendKeys() code is run the element is found again.
But if you add the CacheLookup annotation to the field then the second lookup is not performed but element is returned from the cache. So there is a performance gain. But the problem occurs in a javascript or ajax heavy page, stale element exception can show up.
For any non-trivial application testing use the PageObjectModel framework. Makes things organized and not littered with findElements and locators, even if you do not use the CacheLookUp annotation.
Related
I'm currently in the process of setting up Cypress for my project. Currently we're only using testing library for frontend tests. And reading the Cypress documentation has gotten me a bit confused as the two libraries seem to have opposite philosophies in regards to how you're supposed to query for elements.
Testing library basically says test what the user can see/touch and only use data-testid if all else fails. Cypress on the other hand states that best practice is that you should query elements by data-testid / data-cy attributes.
I feel conflicted between the two approaches. I get the point about we should test what the user actually sees (testing library). But I do also get that those things often change (cypress) and we need to spend time updating tests whenever we make small changes (i.e "Ok" -> "Done"). And when testing with data-cy attributes, are we not also ignoring accessibility / screen readers?
What are your thoughts on this?
React Testing library(RTL) is specifically made to write tests from a user perspective. From their Guiding Principles:
The more your tests resemble the way your software is used, the more confidence they can give you.
Meaning, RTL wants you to use accessibility queries like getByRole and only fallback to getByTestId for cases where you can't match by role or text, or it doesn't make sense (e.g. the text is dynamic).
However, thanks to the render method allowing us to specify props (compared to Cypress), we have much more flexibility and may entirely omit dynamic text.
Cypress, on the other hand, runs with all dependencies. In case of dynamic content from a C.M.S or multi-language application, things are not that easy using getByRole("heading", {name: /welcome/i }). In this case, the recommendation of testId's make sense.
My personal recommendation is to use accessibility query selectors in both Cypress and RTL, unless the text is dynamic. Then testId's in Cypress and a combination of testId's & accessibility query selectors provide the best solution.
It should also be noted that Playwright and Cypress test-generator tools select by accessibility query selectors.
I thought a lot for a few days before answering this question and I even got to do some tests, and after that time I came to the conclusion that the Cypress approach is the best.
The main reason that led me to this answer was that when the testing library says that we should test what the user actually sees, it is already applicable in Cypress even when we use a data-testid, because let's suppose you have a button that exists in the DOM, but it is not visible when you select this button, with the data-testid when you try to click in this button Cypress will return an error saying that the button is not visible and if you really want to do this action you must apply force:true. The same happens if the button is not clickable or if there is another element in front of the button.
Cypress already checks by default in click and type actions if the element:
element is into view
it is visible
it is not disabled
it is not detached
it is not readonly
it is not animating
it is not covered
fire the event at a
descendent
Also if you fetch the element by text, placeholder, or class, this does not guarantee that the element is actually visible to the user, as the element can be in the DOM and not be visible to the user for various reasons.
So the best way to make tests easier to maintain, easier to read, and avoid flaky tests is to use the data-testid and whenever possible or necessary combine the location of the element with an assertion to ensure that the element is visible. Example:
cy.get('[data-testid="button"]').should('be.visible')
I hope I had contributed to the discussion and would love to hear other people's points of view.
[This is cross-posted with SQA - https://sqa.stackexchange.com/questions/35081/using-different-sets-of-page-objects-with-the-same-selenium-tests]
I'm working on a selenium test suite for a set of similar websites. The test suite would be execute separately for each site and the tests in the suite are applicable to all websites.
These websites are built on the same underlying platform, however have different theme/scheme and therefore page objects for these sites need to be different. This is not a case of one or two selectors being different, but majority of selectors different - yet the rest of the structure is the same.
I can easily create two (or three, or whatever) sets of page objects, however I can't figure out how to tell the test executor (Cucumber cli in my case) which page objects to use.
The closest I can come up with is use different package names with common base classes and then individual pages extending from the common base classes. Another possibility is to extract all the selectors into property files and load one or another based on an input parameter, however this would preclude me from using PageFactory. I can also, probably, create separate sets of page objects in different source sets and then compile one or the other based on my need - but, again, I can think of the theory, yet not the execution.
So, what's the right approach and how to achieve this? If it matters, the project is built using gradle.
Since the tests are suitable for all the sites I will assume the only difference between the pages is the selectors behind the objects.
cssSelector and xpath provides or. Assuming text field has ID id1, id2 or id3 depends on the page you can use
#FindBy(how = How.CSS, using = "#id1, #id2, #id3")
private WebElement textField;
This will return the WebElement matching one of the ids.
The solution turned out to be a custom ElementLocator and ElementLocatorFactory with a corresponding custom annotation. It's a bit more work, but it's a very clear solution. I published it on github. In short, the implementation allows to do this:
public class MyPage {
#SearchWith(page="MyPage", name="myElement", locatorsFile="{locators}")
WebElement someElement
...
}
Then run the whole test with -Dlocators=site1.json or -Dlocators=site2.json - and corresponding locators will be loaded from one or the other. It even allows to specify -Dlocators=[site3.json] and load locators from site3.json resource (as obtained using this.getClass().getResourceAsStream(locatorsFile))
I want to evaluate my content blocks before running my test suite but the closures' property names is in bytecode already. I'm ooking for the cleanest solution (compared with parsing source manually).
Already tried solution outlined in this post (and I'd still wind up doing some RegEx/parsing) but could only get it to work via script execution engine. It failed in IDE and GroovyConsole. Rather than embedding a Groovy script in project's code, I thought I'd try using Geb's native classes.
Is building on the suggestion about extending Geb Navigators here viable for Geb's PageContentSupport class whose contentTemplates contain a LinkedHashMap of exactly what I need? If yes, would someone provide guidance? If no, any suggestions?
It is currently not possible to get hold of all content elements for a given page/module. Feel free to create an issue for this in Geb's bug tracker, but remember that all that Geb can provide is either a list of content element names or a map from these names to closures that create these elements.
Having that information isn't a generic solution to your problem because it's possible for content elements to take parameters and there are situations where your content elements will be available on the page only after some other actions are performed (for example you have to click on button to reveal a section of a page that uses ajax to retrieve it's content). So I'm afraid that simply going over all elements and checking if they don't throw any errors will not cut it.
I'm still struggling to see what would "evaluating" all content elements prior to running the suite buy you. Are you after verifying that your content elements still work to get a faster feedback than running the whole suite? I'm pretty sure that you won't be able to fully automate detection of content definitions that don't work anymore. In my view it will be more effort than it's worth.
For automation testing with Selenium, I found using the webdriver.keyboard.senkeys(elementCoordinates) seems like a more reliable way to locate and sendkeys to the element compared to webElement.sendKeys(). Basically, they should all be designed for the same function, but why one could work but sometimes the other doesn't work?
I am using "org.openqa.selenium.phantomjs.PhantomJsDriver" Java class.
At times, I need to identify whether a given WebElement is a particular type of web element; eg: Is this webelement a input type element or not.
This does not seem to come with PhantomJsDriver package.
"com.gargoylesoftware.htmlunit." package seems to have useful wrappers on top of web elements. I can write code like "element instanceof HtmlInput".
Question here is
- Can I really HTMLunit package with phantomjsdriver ? Am I using two libraries which are not supposed to be used with one-another ?
No. Unfortunately, you can't do this the way you're doing it. PhantomJsDriver is backed by WebKit, while HtmlUnitDriver is backed by HtmlUnit which has its own browser core. Selenium is able to wrap both these (and some more) under one hood, but we can't use them interchangeably.
There are, however, different ways of doing what you're trying to do, the best probably being using Selenium's own methods getTagName() and getAttribute() if needed.
If you ran getTagName() on your input element, it would gladly return "input".