What is the difference between the following location techniques?
element(by.id(\"id\"));
element(by.css(\"#id\"));
element(by.id("id"));
element(by.css("#id"));
element(by.xpath("//*[@id='id']"));
browser.executeScript("return document.getElementById('id');");
browser.executeScript("return document.querySelector('#id');");
I will give this a try. I will try to explain it until the point of Protractor
and WebdriverJS
. As I am not writing Selenium
or browser's drivers (E.g. Chromedriver
, Firefoxdriver
... etc...) which is the communication between browsers and Selenium
. For this I will use standard knowledge of browser's engine for this point. Therefore the answer, it will not 100% accurate but mostly.
For the first 3 methods:
element(by.id("id"));
element(by.css("#id"));
element(by.xpath("//*[@id='id']"));
These are all result with a single HTTP request to Selenium server. Which is:
'/session/:sessionId/element/:id/element'
With 2 parameters:
using
: type of :id
. Example: 'css select'
, 'id'
, 'xpath'
, 'link text'
... etc..value
: value of :id
. Example 'element-id'
, '.element-css > .child'
, //*[@id='id']
At this point Selenium
will answer the request accordingly to what is being requested through either of Chromedriver
, Firefoxdriver
... etc...
Under Webdriver find-element-strategy.js . As it is seem like methods are mapped with what browser providing by javascript document
properties. We got tag
, id
, css selector
, xpath
... etc.. which are matched with document.elementByTagName
, document.elementByID
, document.querySelecotr
, document.evaluate
...
Logically in the point of view of a coder I will say the resource should be reuse regardless how these drivers got written. For example quest request for id
it will most likely something like getElementById
got to be trigger at browser side through the communication driver.
=> SUMMARY So in the end we have:
css selector
equivalent of querySelector
id
equivalent of getElementById
xpath
equivalent of evaluate
For 2 last methods:
browser.executeScript("return document.querySelector('#id');");
browser.executeScript("return document.getElementById('id');");
These are all result with a single HTTP request to Selenium server. Which is:
'/session/:sessionId/execute'
With 2 parameters:
script
: javascript text ('string'
) or a function
args
: arguments is an array
Until this point it with be about how JS got injected into browser, as none of us can be sure the behavior (either using devtools
or inject <script>
into HTML). Let's just assume that it will be the same for all browser.
=> SUMMARY So in the end we will analyze :
querySelector
getElementById
element()
vs browser.executeScript()
:=> SUMMARY fast to slow
element()
browser.executeScript()
document.querySelector()
vs document.getElementById()
vs document.querySelector()
:Again each browser will result a slightly difference. But there are already some research about this. I will just use what community has been found.
CSS selector
being know that faster than xpath
(source)
document.querySelector()
> FASTER > document.evaluate()
(NOTE: xpath is not supported by IE, so selenium using its own xpath engine whenever you are using xpath on IE with protractor)
On jsperf.com we got this test that saying
document.getElementById()
> FASTER > document.querySelector()
=> SUMARY fast to slow
document.getElementById()
document.querySelector()
document.evaluate()
element(by.id("id"));
element(by.css("#id"));
element(by.xpath("//*[@id='id']"));
browser.executeScript("return document.getElementById('id');");
browser.executeScript("return document.querySelector('#id');");
Your question is very difficult to answer, certainly to give a single conclusive answer. In fact, I am tempted to flag this question as "too broad", which is supported by the other answers and comments.
Take, for example, just your element(by.id("id"));
. Looking through the Selenium source, most drivers just take whatever id you give, and pass it off to the wire protocol:
public WebElement findElementById(String using) {
if (getW3CStandardComplianceLevel() == 0) {
return findElement("id", using);
} else {
return findElementByCssSelector("#" + cssEscape(using));
}
}
As you know, each browser vendor implements their own wire protocol in a separate binary. Feel free to go further into the code, to dig a deeper hole for your self.
For other browsers that do not support the wire protocol, for example HtmlUnit, you just have something like:
public List<WebElement> findElementsById(String id) {
return findElementsByXPath("//*[@id='" + id + "']");
}
and then they parse the available DOM.
As for your performance question, anything that anyone gives you will be 1) just a feeling, or 2) pure BS! Which you can already see from the other answers and comments you are getting.
To get a real answer (supported by actual data), there are just too many variables to consider:
Also, whatever results you get for your web app / web page will most like not apply to a different web app / web page, due to differences in the framework used to build that site.
Bottom line is: If you are concerned about performance testing, then Selenium is the wrong answer. Selenium is a functional test library, optimized to give you the best end-user representation. Performance is a distant afterthought.
If your goal is to get your tests to run faster, your time will be better spent looking at your test structure:
But I think this is getting off topic (some might suggest "ranty") from your initial question.
It would be a broad answer if someone tries to answer it so I will try to make it as simple as I can.
These are just different ways of finding elements using selenium. The reason why we have so many alternatives to select elements is, not always we will have id or class tagged to an element. For elements which do not have id or class or name the only option left with us is XPATH.
XPATH can be used to uniquely identify any element in an XML and since HTML (HTML 5 to be precise, if written according to standards) is an instance of XML we can use XPATH to uniquely identify every element in the file.
OK so why not use XPATH all the time? Why so many alternatives? Simple, XPATH is difficult to write. For example if we need to get the XPATH of a 'td' which belongs to a table nested inside 2 other tables. XPATH will be pretty long and most of the time we tend to make a mistake.
Finding XPATH in firefox is pretty straight forward, simply install firepath or firebug and right click on the element and select COPY XPATH.
Detailed instructions on indicators in selenium: here (presented in java but will help in general)
I just think about performance perspective and write the following script to check google logo. Although the result is confusing, we can make an estimation the result statistically.
querySelector and getElementById have always better result, unless number of trials is more than 10K. If we compare these two method: getElementById is better (29 over 21).
If we compare these there, ID, CSS and XPATH, CSS is better (29 over 18 and 4), second is ID and last one XPATH.
The result from my test: getElementById, querySelector, CSS, ID, XPATH
See the table, result and script:
Table show the result in summary for 50 tries:
1 2 3 4 5 ID 0 0 18 24 8 CSS 0 0 29 18 3 XPATH 0 0 4 12 34 querySelector 21 29 0 0 0 getElementById 29 21 0 0 0
Result with time diff:
>>> for i in range(50):
... test_time(1)
...
[('getElementById', 0.004777193069458008), ('querySelector', 0.006440162658691406), ('id', 0.015267133712768555), ('css', 0.015399932861328125), ('xpath', 0.015429019927978516)]
[('querySelector', 0.006442070007324219), ('getElementById', 0.00728607177734375), ('id', 0.013181924819946289), ('css', 0.014509916305541992), ('xpath', 0.015583992004394531)]
[('getElementById', 0.0063440799713134766), ('querySelector', 0.006493091583251953), ('css', 0.014523029327392578), ('id', 0.014902830123901367), ('xpath', 0.015790224075317383)]
[('getElementById', 0.007112026214599609), ('querySelector', 0.007357120513916016), ('id', 0.014781951904296875), ('css', 0.015780925750732422), ('xpath', 0.016005992889404297)]
[('getElementById', 0.006434917449951172), ('querySelector', 0.007117033004760742), ('id', 0.01497507095336914), ('css', 0.015005111694335938), ('xpath', 0.015393972396850586)]
[('querySelector', 0.00563812255859375), ('getElementById', 0.006503105163574219), ('css', 0.014302968978881836), ('id', 0.014812946319580078), ('xpath', 0.017061948776245117)]
[('querySelector', 0.0048770904541015625), ('getElementById', 0.006540060043334961), ('css', 0.014795064926147461), ('id', 0.015192985534667969), ('xpath', 0.016000986099243164)]
[('getElementById', 0.006265878677368164), ('querySelector', 0.006501913070678711), ('id', 0.014132022857666016), ('css', 0.01437997817993164), ('xpath', 0.014840841293334961)]
[('getElementById', 0.006368160247802734), ('querySelector', 0.006601095199584961), ('css', 0.01462101936340332), ('id', 0.014872074127197266), ('xpath', 0.016145944595336914)]
[('querySelector', 0.00642704963684082), ('getElementById', 0.006908893585205078), ('css', 0.014439105987548828), ('id', 0.014970064163208008), ('xpath', 0.015510082244873047)]
[('getElementById', 0.006404876708984375), ('querySelector', 0.006679058074951172), ('css', 0.014878988265991211), ('id', 0.01546788215637207), ('xpath', 0.015535116195678711)]
[('querySelector', 0.005848884582519531), ('getElementById', 0.008013010025024414), ('css', 0.014436006546020508), ('xpath', 0.01566910743713379), ('id', 0.015830039978027344)]
[('querySelector', 0.006299018859863281), ('getElementById', 0.006538867950439453), ('css', 0.014534950256347656), ('id', 0.014979124069213867), ('xpath', 0.01618194580078125)]
[('getElementById', 0.006415128707885742), ('querySelector', 0.006479978561401367), ('id', 0.014901876449584961), ('css', 0.014998912811279297), ('xpath', 0.01544499397277832)]
[('getElementById', 0.006515979766845703), ('querySelector', 0.006515979766845703), ('xpath', 0.014292001724243164), ('css', 0.014482975006103516), ('id', 0.015102863311767578)]
[('getElementById', 0.00574803352355957), ('querySelector', 0.006389141082763672), ('css', 0.014650821685791016), ('id', 0.014751911163330078), ('xpath', 0.01532888412475586)]
[('getElementById', 0.0063037872314453125), ('querySelector', 0.006974935531616211), ('id', 0.014775991439819336), ('css', 0.014935970306396484), ('xpath', 0.015460968017578125)]
[('getElementById', 0.0064661502838134766), ('querySelector', 0.0065021514892578125), ('id', 0.014723062515258789), ('css', 0.014946937561035156), ('xpath', 0.015508890151977539)]
[('getElementById', 0.006738901138305664), ('querySelector', 0.008143901824951172), ('css', 0.014575004577636719), ('xpath', 0.015228986740112305), ('id', 0.015702009201049805)]
[('getElementById', 0.006436824798583984), ('querySelector', 0.0064470767974853516), ('css', 0.014545917510986328), ('id', 0.014694929122924805), ('xpath', 0.015357017517089844)]
[('querySelector', 0.006292104721069336), ('getElementById', 0.006451845169067383), ('css', 0.014657020568847656), ('xpath', 0.01574397087097168), ('id', 0.016795873641967773)]
[('getElementById', 0.006443977355957031), ('querySelector', 0.006485939025878906), ('css', 0.013139009475708008), ('id', 0.014308929443359375), ('xpath', 0.015516042709350586)]
[('querySelector', 0.006464958190917969), ('getElementById', 0.006821870803833008), ('id', 0.016110897064208984), ('css', 0.01633286476135254), ('xpath', 0.017225980758666992)]
[('getElementById', 0.005715131759643555), ('querySelector', 0.008069992065429688), ('css', 0.014779090881347656), ('id', 0.01491093635559082), ('xpath', 0.015527963638305664)]
[('getElementById', 0.006309986114501953), ('querySelector', 0.006836891174316406), ('css', 0.01497507095336914), ('id', 0.015040159225463867), ('xpath', 0.02096104621887207)]
[('querySelector', 0.00616908073425293), ('getElementById', 0.007357120513916016), ('css', 0.014974832534790039), ('id', 0.015640974044799805), ('xpath', 0.016278982162475586)]
[('querySelector', 0.005301952362060547), ('getElementById', 0.0063440799713134766), ('id', 0.014526844024658203), ('css', 0.014657974243164062), ('xpath', 0.0162200927734375)]
[('querySelector', 0.005811929702758789), ('getElementById', 0.007221221923828125), ('css', 0.01259613037109375), ('xpath', 0.014851093292236328), ('id', 0.015043020248413086)]
[('getElementById', 0.006195068359375), ('querySelector', 0.007548093795776367), ('css', 0.01441502571105957), ('id', 0.01441812515258789), ('xpath', 0.016713857650756836)]
[('querySelector', 0.0050449371337890625), ('getElementById', 0.006323099136352539), ('id', 0.01497793197631836), ('css', 0.014984130859375), ('xpath', 0.015444040298461914)]
[('getElementById', 0.007039070129394531), ('querySelector', 0.008107900619506836), ('xpath', 0.015566825866699219), ('id', 0.015954017639160156), ('css', 0.01815509796142578)]
[('getElementById', 0.005831003189086914), ('querySelector', 0.007988214492797852), ('id', 0.014652013778686523), ('css', 0.014683008193969727), ('xpath', 0.01581597328186035)]
[('querySelector', 0.006363868713378906), ('getElementById', 0.006494998931884766), ('xpath', 0.01517796516418457), ('id', 0.016071796417236328), ('css', 0.017260074615478516)]
[('getElementById', 0.00633692741394043), ('querySelector', 0.007826089859008789), ('css', 0.014354944229125977), ('id', 0.015484809875488281), ('xpath', 0.017076969146728516)]
[('querySelector', 0.006349802017211914), ('getElementById', 0.006428956985473633), ('css', 0.01385188102722168), ('id', 0.014858007431030273), ('xpath', 0.016836166381835938)]
[('querySelector', 0.006417989730834961), ('getElementById', 0.007012844085693359), ('css', 0.01460719108581543), ('id', 0.014763832092285156), ('xpath', 0.015476226806640625)]
[('getElementById', 0.006266117095947266), ('querySelector', 0.0074520111083984375), ('id', 0.014987945556640625), ('css', 0.01515817642211914), ('xpath', 0.015646934509277344)]
[('getElementById', 0.006376981735229492), ('querySelector', 0.0064089298248291016), ('id', 0.01494598388671875), ('css', 0.015275001525878906), ('xpath', 0.01553201675415039)]
[('getElementById', 0.006357908248901367), ('querySelector', 0.006699085235595703), ('css', 0.014505147933959961), ('xpath', 0.015446186065673828), ('id', 0.019747018814086914)]
[('getElementById', 0.0063610076904296875), ('querySelector', 0.0064640045166015625), ('css', 0.014472007751464844), ('id', 0.014828205108642578), ('xpath', 0.01532888412475586)]
[('getElementById', 0.0063610076904296875), ('querySelector', 0.006580829620361328), ('css', 0.012439966201782227), ('id', 0.014935016632080078), ('xpath', 0.015373945236206055)]
[('querySelector', 0.006309032440185547), ('getElementById', 0.006561994552612305), ('id', 0.014923095703125), ('css', 0.015380859375), ('xpath', 0.01574110984802246)]
[('querySelector', 0.006357908248901367), ('getElementById', 0.006387948989868164), ('css', 0.01481485366821289), ('id', 0.015089988708496094), ('xpath', 0.015390872955322266)]
[('querySelector', 0.004536867141723633), ('getElementById', 0.00640416145324707), ('css', 0.014551877975463867), ('xpath', 0.014974117279052734), ('id', 0.014991998672485352)]
[('getElementById', 0.006387233734130859), ('querySelector', 0.00643610954284668), ('css', 0.014494895935058594), ('id', 0.014873981475830078), ('xpath', 0.015212059020996094)]
[('getElementById', 0.0063588619232177734), ('querySelector', 0.006443977355957031), ('css', 0.013200998306274414), ('id', 0.014631986618041992), ('xpath', 0.015624046325683594)]
[('getElementById', 0.0048558712005615234), ('querySelector', 0.005300045013427734), ('id', 0.014750003814697266), ('css', 0.014846086502075195), ('xpath', 0.015408992767333984)]
[('querySelector', 0.008347034454345703), ('getElementById', 0.008370161056518555), ('id', 0.014650106430053711), ('css', 0.014775991439819336), ('xpath', 0.015323877334594727)]
[('querySelector', 0.006309032440185547), ('getElementById', 0.007323026657104492), ('css', 0.014546871185302734), ('xpath', 0.015864133834838867), ('id', 0.02078390121459961)]
[('querySelector', 0.007790088653564453), ('getElementById', 0.010209083557128906), ('id', 0.015320062637329102), ('xpath', 0.01600193977355957), ('css', 0.01807403564453125)]
Code:
from timeit import default_timer as timer
import time, operator
from selenium import webdriver
def open_browser():
dr = webdriver.Chrome()
dr.get('http://www.google.com')
time.sleep(5)
return dr,timer()
def quit_browser(el, start, dr, result):
diff = timer() - float(start)
result[el] = diff
dr.quit()
def test_time(tm):
result = {}
dr, start = open_browser()
for i in range(tm):
dr.find_element_by_id('hplogo')
quit_browser("id", start, dr, result)
dr, start = open_browser()
for i in range(tm):
dr.find_element_by_css_selector('#hplogo')
quit_browser("css", start, dr, result)
dr, start = open_browser()
for i in range(tm):
dr.find_element_by_xpath("//*[@id='hplogo']")
quit_browser("xpath", start, dr, result)
dr, start = open_browser()
for i in range(tm):
dr.execute_script("return document.querySelector('#hplogo');")
quit_browser("querySelector", start, dr, result)
dr, start = open_browser()
for i in range(tm):
dr.execute_script("return document.getElementById('hplogo');")
quit_browser("getElementById", start, dr, result)
print sorted(result.items(), key=operator.itemgetter(1))
Identifying differences would be pretty tough. Here are few things that i found -
executeScript() schedules a command to execute JavaScript as a string in the context of the currently selected frame or window. Though this is fast, readability of code is low.
element()
function in turn resolves to findElement() function which schedules a command to find the element on the DOM. Improved readability.
From performance perspective according to me, here are the rankings in ascending order starting with the fastest and all of them were close to each other with differences in few milliseconds -
1 - browser.executeScript("return document.getElementById('id');");
2 - browser.executeScript("return document.querySelector('#id');");
3 - element(by.id("id"));
4 - element(by.css("#id"));
5 - element(by.xpath("//*[@id='id']"));
The reason for the javascript executeScript()
being so fast is because the command its executed on DOM directly with no conversions. This link justifies their rankings between each other.
The remaining protractor specific element()
locators are slow as protractor needs to convert the commands to get the web-elements using findElement()
function. Getting element by id
is faster than using css
and xpath
(This is also dependent on how the locators are being used and often can change based on usage).
NOTE: The above performance analysis was the average of many tests that i ran locally on my machine, but it can differ based on system tasks that internally affect running the test scripts.
Hope it helps.