Started with Angular and Protractor.
It just feels wrong to write some heavy css selectors which will break instant when you change something.
Using ID's would make testing way easier.
I'm not using any id attribute for styling yet. Are there any drawbacks using ids for testing I haven't considered?
The general rule is to use IDs whenever possible assuming they are unique across the DOM and not dynamically generated. Quoting Jim Holmes:
Whenever possible, use ID attributes. If the page is valid HTML, then
IDs are unique on the page. They're extraordinarily fast for
resolution in every browser, and the UI can change dramatically but
your script will still locate the element.
Sometimes IDs aren't the right choice. Dynamically generated IDs are
almost always the wrong choice when you're working with something like
a grid control. You rely on an id that is likely tied to the specific
row position and then you're screwed if your row changes.
Also, in general try to use the "data-oriented" approach: by.model, by.binding, by.repeater locators, or if you rely on class names, choose them wisely: do not use layout-oriented classes like .col-xs-4 or .container-fluid.
See also these related topics:
Best Practices for Watir and Selenium Locators
best way to detect an element on a web page for seleniumRC in java
Related
That's obvious that for the diffing algorithm in React keys are essential. But I was wandering, why React just can't automatically generate keys based on the content we iterate over?
I also assume that items can share some similarity, or cab be identical in terms of content, but isn't it possible to generate keys once user open a page and somehow attach them to the items, so it is stable?
Or maybe there where attempts to solve the problem, if so, I would be grateful if you share it to me.
Update
Thank you guys for your answers, I've learnt a lot!
Also a thing I had in mind: what we developers do when there is no stable id (e.g. user added an item which is not yet saved into DB). In the cases we just generate id, and attach it to the object, or element in an array, but we do not generate ids on a fly, so it remains stable over time.
What if React just generate ids for all arrays which are involved into rendering process, in other words, arrays which are directly used in render function?
It can be done only once, during phase Commit phase, or whatever. Also I believe, the id can be readonly, or something, so user can't erase the id.
p.s.s
While I was writing p.s. question above, I realized, autogenerating id for arrays wouldn't work, since I've missed two things. All side effect react can do only during the Commit phase, but not Render phase. But that's not the main problem.
The main problem is when we use filtering or sorting on a back-end side. Since we receive a new array, filtered one, we would need to regenerate ids for those elements, but basically, that's the same html elements, in which we can change content to match filtering order. That's the same as Slava Knyazev mentioned.
React can't generate keys, because the entire point of keys is for you to help React track elements during it's tree-diffing stage.
For example, lets say you have the following code, where you naively use content instead of identifiers for your keys:
const people = usePeople(); // [{ id: "1", name: "James"}, {id: "2", name: "William"}]
return <ul>{people.map(p => <li key={p.name}>{p.name}</li>}</ul>
The above code will function and behave as you would expect. But what happens if the name of a person changes? To understand it, lets look at the tree it generates:
ul
li(James) James
li(William) William
If James becomes Josh between renders, the new tree will look like this:
ul
li(Josh) Josh
li(William) William
React will compare the two results and conclude the following:
li(James) is to be removed
li(Josh) is to be added
However, if we set our key prop to p.id, then the old and new tree will look as follows, respectively:
ul
li(1) James
li(2) William
ul
li(1) Josh
li(2) William
And when React compares the two, it will identify that James has become Josh, and needs only the text adjusted.
In the first scenario, the <li> component is completely destroyed, and a completely new component takes its place. Both of these actions run a complete React lifecycle for the component. In the second, it remains untouched, and only the text inside changes.
While in this contrived scenario, the performance penalty in the first case in minimal, it may be very significant with complex components.
I believe, unless your data is 100% certainly going to sort in one way and never change, key={index} isn't a good key (which is what I assume you want your auto-generated keys to be). You'd ideally want something that is unique to each item, regardless of the order.
It's explained in more detail in the new beta react docs https://beta.reactjs.org/learn/rendering-lists#where-to-get-your-key
I think what you are implying is React could potentially choose to use something like a stable hash (say sha1 on a serialised string or something) on the object to generate a unique key. I think this actually would work in many cases, and even gave me pause for thought for a while! Your question is actually a really good one, and a deep one.
However, it wouldn't work in every case. I think it would work only on a static object which has no methods or anything attached. On a JS object, not all properties are enumerable. Hashing something could only ever happen on the enumerable objects of properties, but the dev may have non-enumerable yet-still-unique methods attached to these objects. In fact, even enumerable methods cant really be serialised reliably and they could be what makes the object unique. Not to mention the complexities of reliably hashing something with prototypical inheritance involved.
I suspect there's also a performance aspect to this. Hashing is cheap, but no that cheap. Most cases can be keyed by just referencing a unique ID in the object, which is vastly cheaper. When enumerating a very large number of objects, these things matter, and so its better to defer to the developer. After all, if you really do want to hash it, its just one function call in userland -- and this saves great confusion on developer side when it doesn't work. The Principle of least astonishment comes to mind.
There's also an aspect of how this would limit the power of how expressive JSX can be due to it basically allowing free-form JS. You would probably have to supply some low level <React.Map> component primitives in order for react to supply this default key handling which implies you are a bit more restrained on what you can and can't do (complex functional chains).
Cypress and many other posts around testing web applications suggest relying on a data attribute like data-cy or data-test-id for locating elements rather than relying on the id attribute.
My understanding is that for two reasons:
The modern way of re-using the components can lead to having multiple components of the same type and can lead to multiple of those IDs on the same page - But this should also apply to the 'data-cy' or 'data-test-id' attributes.
When IDs are tied to CSS, there's a tendency to change them more often while data-* attributes may be less prone to change.
Can someone please throw more light on the recommendation?
The other thing I am considering is to request my devs to place the data-test* attributes on a div tag that would consume the component - that way the test attribute is actually one level above the component id attribute and may come handy even in cases where multiple instances of the same component are used. But again, I am not sure why the id attribute for that div tag is bad when compared to the data-test* attribute.
From Cypress official docs:
Anti-Pattern: Using highly brittle selectors that are subject to change.
Best Practice: Use data-* attributes to provide context to your selectors and isolate them from CSS or JS changes.
Every test you write will include selectors for elements. To save yourself a lot of headaches, you should write selectors that are resilient to changes.
Oftentimes we see users run into problems targeting their elements because:
Your application may use dynamic classes or ID's that change
Your selectors break from development changes to CSS styles or JS behavior
Luckily, it is possible to avoid both of these problems.
Don't target elements based on CSS attributes such as: id, class, tag
Don't target elements that may change their textContent
Add data-* attributes to make it easier to target elements
The point is that id's and classes can be dynamic (also text-content) so you always want to use a selector that is static like the "data-cy" attribute.
Our team is starting with Angular JS development and for testing we are using Jasmine-Karma-Protractor. The debate is whether we should use IDs for accessing DOM elements or use XPATH/CSS/Binding to access the elements since Angular doesn't really enforce use of IDs.
It is a question of best practice and I want to know what is recommended by the community. Please share what strategy is followed in your team.
Thanks.
Update
After going through all your answers and comments I realize that there is no specifically right or wrong way to do this. (Personally I am leaning towards using IDs!) We will have a meeting to discuss the approach we want and decide whether to use IDs or not. Thank you all very much for your help.
Of course you should use IDs. That will make your tests much easier to write and read, faster, and most importantly, more robust: the test won't have to change every time the markup on the page changes for whatever reason (style, new layout, etc.).
It's much clearer to say "find the menu item 'customers'" than to say "find the 4th li contained in the third ul contained in the second div of the page".
As i have worked on Protractor for 2 years and designed good POM testing framework from the scratch, i can suggest you below order for Angularjs application:
Order:
by.model()
by.repeater()
by.css()
by.id()
by.name()
by.xpath() - least preference
I can also add 2 more options to the existing answers. :
by.className();
by.tagName();
by.cssSelector();
in case there is no fix id in your html code!
I have some data on a model that comes in the form of a code such as "US60" and "US70".
I need to take that value and show a display value such as "US 7day/60hour" and "US 8day/70hour". I'm not sure if there is any best practices way to do this in Angular, and I'm not having much luck googling it.
What I would do is have a service that I pass in type and value, and it would return a display value, but as with many things in Angular, since this is my first Angular project, I don't know if it's a good way to do it or not.
I'm just needing to use the display value in html such as {{settings.cycle}} I am already able to access the variable, but I want to show the display value, not the actual value.
If I am getting the gist of your question correctly, you have the value available but want to alter how it is displayed on screen right?
There are two main approaches to do this in Angular, using a directive or a filter.
A filter is basically like a pipe in Unix. You can alter a value before it is being displayed. For example:
{ username | uppercase } will transform the username into an all-caps username. Naturally, you can define your own filters for your use case. Filters are mostly used to transform single values. So for your case, a filter sounds best.
A directive is commonly used to create entire components on a page. For example: <user-profile-card></user-profile-card> would be transformed, using the directive, into the appropriate html/css/logic. So these are used often for larger transformations which involve logic, like server requests. Still these directives could also be used for very small components.
So for your case, although what you are actually want to do is not completely clear to me honestly, a filter seems to be your best shot ;)
Our ui automation team is asking for a better way to select elements for their automated tests. My thinking is that we can inject a dedicated attribute (say "ui-auto") for each testable element. This attribute would have a value which is:
unique
persistent (doesn't change across sessions or page loads so as to not break the tests)
predictable (follows some naming convention depending on action type, location, etc.)
My questions are:
Is this a good idea? better ideas are welcome.
Are there existing conventions for this?
What the best way to implement
this?
I should mention that we are using angular and I thought that
using some kind of directive and/or service would help automate
this.
I should also say that I don't want to use the "id" attribute b/c I'd like to have separation between development concerns (ids may be used for javascript), and qa concerns (selection of elements for automated tests)
In our implementation we add to the DOM element a data-awt attribute, the value consists of a context (page and mode) type and unique string. As we use the EXTJS library our type is the xtype and the unique string is components name or text property. The context is developer controlled by placing a unique property on the upper most parent and all children use this as their context.
In practice we end up with data-awt values like devicesListing-button-edit, deviceDetails-displayfield-name, deviceDetailsEditWindow-textfield-name.
We found that relying on css, id, or other attributes aren't reliable and predictable since we don't want to rewrite our tests whenever there is some UI change. Now the test only needs updating if an existing element changes its name (for example the PM says the name field should now use the 'customer' data from the DTO).
You can also use the class of the element and provide a unique identifier prefixed with something like "auto_" or "t_".
The agreement exists that if anyone changes the class name with that prefix, tests will break.
#o4ohel I agree that not using ids is better as devs also depend on them and they need to change sometimes. Identifiers for automation should be isolated. It's nice to have that separation.