Locating an element by id

前端 未结 5 925
清歌不尽
清歌不尽 2020-12-01 08:16

What is the difference between the following location techniques?

  1. element(by.id(\"id\"));
  2. element(by.css(\"#id\"));
  3. <
相关标签:
5条回答
  • 2020-12-01 08:20

    TL;DR; performance in order from fast to slow.

    • 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.

    I will separate 5 methods in 2 groups:

    1. Common communications

    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

    2. Injecting communications

    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

    Main comparison

    1. element() vs browser.executeScript():

    • element() Original made to locate elements. It's using all the method profile by browser and it communication driver. This will result as faster performance
    • browser.executeScript() was original not made to use to locating an element. But to execute a script, by using it we are of course getting same result but going through more complicating way to achieve the same goal. Therefore this will result in a more complicated calculation than using element finder. And ended up result a slower performance.

    => SUMMARY fast to slow

    • element()
    • browser.executeScript()

    2. 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()

    3. Sum up performance of method from fast to slow

    • 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');");
    0 讨论(0)
  • 2020-12-01 08:27

    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:

    • Wire protocol as implemented by different browser vendors, plus various optimizations in different versions.
    • DOM engines as implemented by different browser vendors, plus various optimizations in different versions.
    • JavaScript engines as implemented by different browser vendors, plus various optimizations in different versions.

    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:

    • How frequently do you open/close the browser. This is often the most time consuming activity in a test.
    • How often do you refresh your element cache, how often do you need to? Consider moving your elements to Page Factory model, which lazy-loads all elements for you.
    • And of course the biggest speedup factor: running your tests in parallel on multiple machines.

    But I think this is getting off topic (some might suggest "ranty") from your initial question.

    0 讨论(0)
  • 2020-12-01 08:30

    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)

    0 讨论(0)
  • 2020-12-01 08:34

    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))
    
    0 讨论(0)
  • 2020-12-01 08:39

    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.

    0 讨论(0)
提交回复
热议问题