Bất kỳ đề xuất nào để kiểm tra mã extjs trong trình duyệt, tốt nhất là với selen?


92

Chúng tôi đã sử dụng selen rất thành công để xử lý thử nghiệm trang web cấp cao (ngoài các học thuyết python mở rộng ở cấp mô-đun). Tuy nhiên, hiện tại chúng tôi đang sử dụng extj cho rất nhiều trang và việc kết hợp các bài kiểm tra Selenium cho các thành phần phức tạp như lưới là rất khó.

Có ai đã thành công khi viết các bài kiểm tra tự động cho các trang web dựa trên extjs? Rất nhiều googling tìm thấy những người có vấn đề tương tự, nhưng rất ít câu trả lời. Cảm ơn!


Những người tốt tại Ext JS đã đủ tử tế để đăng về chủ đề này trên blog của họ ở đây . Tôi hi vọng cái này giúp được.
NBRed

Câu trả lời:


173

Rào cản lớn nhất trong việc thử nghiệm ExtJS với Selenium là ExtJS không hiển thị các phần tử HTML tiêu chuẩn và Selenium IDE sẽ tạo ra các lệnh một cách ngây thơ (và đúng đắn) nhằm vào các phần tử chỉ hoạt động như trang trí - các phần tử thừa giúp ExtJS với toàn bộ màn hình- xem và cảm nhận. Dưới đây là một số mẹo và thủ thuật mà tôi đã thu thập được khi viết thử nghiệm Selenium tự động với ứng dụng ExtJS.

Mẹo chung

Định vị các phần tử

Khi tạo các trường hợp kiểm tra Selenium bằng cách ghi lại các hành động của người dùng với Selenium IDE trên Firefox, Selenium sẽ căn cứ các hành động đã ghi trên id của các phần tử HTML. Tuy nhiên, đối với hầu hết các phần tử có thể nhấp, ExtJS sử dụng các id được tạo như "ext-gen-345" có khả năng thay đổi trong lần truy cập tiếp theo vào cùng một trang, ngay cả khi không có thay đổi mã nào được thực hiện. Sau khi ghi lại các thao tác của người dùng để kiểm tra, cần phải có một nỗ lực thủ công để thực hiện tất cả các thao tác phụ thuộc vào id đã tạo và để thay thế chúng. Có hai loại thay thế có thể được thực hiện:

Thay thế Bộ định vị ID bằng Bộ định vị CSS hoặc XPath

Trình định vị CSS bắt đầu bằng "css =" và trình định vị XPath bắt đầu bằng "//" (tiền tố "xpath =" là tùy chọn). Bộ định vị CSS ít dài dòng hơn và dễ đọc hơn và nên được ưu tiên hơn bộ định vị XPath. Tuy nhiên, có thể có những trường hợp cần sử dụng bộ định vị XPath vì bộ định vị CSS đơn giản là không thể cắt nó.

Thực thi JavaScript

Một số yếu tố yêu cầu nhiều hơn các tương tác chuột / bàn phím đơn giản do kết xuất phức tạp do ExtJS thực hiện. Ví dụ, một Ext.form.CombBox không thực sự là một <select>phần tử mà là một đầu vào văn bản với danh sách thả xuống tách rời nằm ở đâu đó ở cuối cây tài liệu. Để mô phỏng chính xác lựa chọn ComboBox, trước tiên có thể mô phỏng một lần nhấp vào mũi tên thả xuống và sau đó nhấp vào danh sách xuất hiện. Tuy nhiên, việc định vị các phần tử này thông qua các trình định vị CSS hoặc XPath có thể phức tạp. Một giải pháp thay thế là định vị chính thành phần ComoBox và gọi các phương thức trên đó để mô phỏng lựa chọn:

var combo = Ext.getCmp('genderComboBox'); // returns the ComboBox components
combo.setValue('female'); // set the value
combo.fireEvent('select'); // because setValue() doesn't trigger the event

Trong Selenium, runScriptlệnh có thể được sử dụng để thực hiện thao tác trên ở dạng ngắn gọn hơn:

with (Ext.getCmp('genderComboBox')) { setValue('female'); fireEvent('select'); }

Đối phó với AJAX và Kết xuất chậm

Selenium có hương vị "* AndWait" cho tất cả các lệnh chờ tải trang khi hành động của người dùng dẫn đến chuyển đổi trang hoặc tải lại. Tuy nhiên, vì các lần tìm nạp AJAX không liên quan đến tải trang thực tế, nên không thể sử dụng các lệnh này để đồng bộ hóa. Giải pháp là sử dụng các manh mối trực quan như sự hiện diện / vắng mặt của chỉ báo tiến trình AJAX hoặc sự xuất hiện của các hàng trong lưới, các thành phần bổ sung, liên kết, v.v. Ví dụ:

Command: waitForElementNotPresent
Target: css=div:contains('Loading...')

Đôi khi một phần tử sẽ chỉ xuất hiện sau một khoảng thời gian nhất định, tùy thuộc vào tốc độ ExtJS hiển thị các thành phần sau khi một hành động của người dùng dẫn đến thay đổi chế độ xem. Thay vì sử dụng độ trễ trọng tài với pauselệnh, phương pháp lý tưởng là đợi cho đến khi yếu tố quan tâm nằm trong tầm nắm bắt của chúng ta. Ví dụ, để nhấp vào một mục sau khi đợi nó xuất hiện:

Command: waitForElementPresent
Target: css=span:contains('Do the funky thing')
Command: click
Target: css=span:contains('Do the funky thing')

Việc dựa vào các khoảng tạm dừng tùy ý không phải là một ý tưởng hay vì sự khác biệt về thời gian dẫn đến việc chạy các bài kiểm tra trong các trình duyệt khác nhau hoặc trên các máy khác nhau sẽ làm cho các trường hợp kiểm tra không ổn định.

Các mục không thể nhấp

Một số phần tử không thể được kích hoạt bằng clicklệnh. Đó là bởi vì trình xử lý sự kiện thực sự ở trên vùng chứa, theo dõi các sự kiện chuột trên các phần tử con của nó, cuối cùng sẽ bong bóng đến cha mẹ. Điều khiển tab là một ví dụ. Để nhấp vào một tab, bạn phải mô phỏng một mouseDownsự kiện tại nhãn tab:

Command: mouseDownAt
Target: css=.x-tab-strip-text:contains('Options')
Value: 0,0

Xác thực trường

Các trường biểu mẫu (thành phần Ext.form. *) Có các biểu thức chính quy được liên kết hoặc các loại vtype để xác thực sẽ kích hoạt xác thực với độ trễ nhất định (xem thuộc validationDelaytính được đặt thành 250ms theo mặc định), sau khi người dùng nhập văn bản hoặc ngay lập tức khi trường bị mất tiêu điểm - hoặc làm mờ (xem thuộc validateOnDelaytính). Để kích hoạt xác thực trường sau khi phát lệnh loại Selenium để nhập một số văn bản bên trong trường, bạn phải thực hiện một trong các thao tác sau:

  • Kích hoạt xác thực bị trì hoãn

    ExtJS kích hoạt bộ đếm thời gian trễ xác thực khi trường nhận được các sự kiện keyup. Để kích hoạt bộ đếm thời gian này, chỉ cần đưa ra một sự kiện keyup giả (không quan trọng bạn sử dụng phím nào vì ExtJS bỏ qua nó), sau đó là một khoảng dừng ngắn dài hơn validationDelay:

    Command: keyUp
    Target: someTextArea
    Value: x
    Command: pause
    Target: 500
    
  • Kích hoạt xác thực ngay lập tức

    Bạn có thể đưa một sự kiện mờ vào trường để kích hoạt xác thực ngay lập tức:

    Command: runScript
    Target: someComponent.nameTextField.fireEvent("blur")
    

Kiểm tra kết quả xác thực

Sau khi xác thực, bạn có thể kiểm tra sự hiện diện hay vắng mặt của trường lỗi:

Command: verifyElementNotPresent   
Target: //*[@id="nameTextField"]/../*[@class="x-form-invalid-msg" and not(contains(@style, "display: none"))]

Command: verifyElementPresent   
Target: //*[@id="nameTextField"]/../*[@class="x-form-invalid-msg" and not(contains(@style, "display: none"))]

Lưu ý rằng việc kiểm tra "display: none" là cần thiết vì một khi trường lỗi được hiển thị và sau đó nó cần được ẩn đi, ExtJS sẽ chỉ ẩn trường lỗi thay vì xóa hoàn toàn trường đó khỏi cây DOM.

Mẹo dành riêng cho từng yếu tố

Nhấp vào một Ext.form.Button

  • lựa chọn 1

    Lệnh: bấm vào Mục tiêu: css = button: chứa ('Lưu')

    Chọn nút theo chú thích của nó

  • Lựa chọn 2

    Lệnh: nhấp vào nút Target: css = # save-options

    Chọn nút theo id của nó

Chọn giá trị từ Ext.form.ComboBox

Command: runScript
Target: with (Ext.getCmp('genderComboBox')) { setValue('female'); fireEvent('select'); }

Đầu tiên đặt giá trị và sau đó kích hoạt rõ ràng sự kiện được chọn trong trường hợp có người quan sát.


3
Dưới đây là một vài lời khuyên của nhãn hiệu: rkapse.blogspot.nl/2009/06/selenium-ide-issues-with-extjs.html
dan-Klasson

5

Blog này đã giúp tôi rất nhiều. Anh ấy đã viết khá nhiều về chủ đề và có vẻ như nó vẫn còn hoạt động. Anh chàng dường như cũng đánh giá cao thiết kế tốt.

Về cơ bản, anh ấy nói về việc sử dụng gửi javascript để thực hiện các truy vấn và sử dụng phương thức Ext.ComponentQuery.query để truy xuất nội dung giống như cách bạn làm trong ứng dụng ext của mình trong nội bộ. Bằng cách đó, bạn có thể sử dụng xtypes và itemIds và không phải lo lắng về việc cố gắng phân tích cú pháp bất kỳ nội dung nào được tạo tự động điên cuồng.

Tôi thấy bài viết này đặc biệt rất hữu ích.

Có thể sẽ sớm đăng điều gì đó chi tiết hơn một chút ở đây - tôi vẫn đang cố gắng tìm hiểu cách thực hiện điều này đúng cách


4

Tôi đã thử nghiệm ứng dụng web ExtJs của mình với selen. Một trong những vấn đề lớn nhất là chọn một mục trong lưới để làm gì đó với nó.

Đối với điều này, tôi đã viết phương thức trợ giúp (trong lớp SeleniumExtJsUtils là tập hợp các phương thức hữu ích để tương tác dễ dàng hơn với ExtJs):

/**
 * Javascript needed to execute in order to select row in the grid
 * 
 * @param gridId Grid id
 * @param rowIndex Index of the row to select
 * @return Javascript to select row
 */
public static String selectGridRow(String gridId, int rowIndex) {
    return "Ext.getCmp('" + gridId + "').getSelectionModel().selectRow(" + rowIndex + ", true)";
}

và khi tôi cần chọn một hàng, tôi chỉ cần gọi:

selenium.runScript( SeleniumExtJsUtils.selectGridRow("<myGridId>", 5) );

Để điều này hoạt động, tôi cần đặt id của mình trên lưới và không để các ExtJ tự tạo ra id.


bạn sẽ có thể tìm thấy lưới bằng cách sử dụng xtype của nó trong Ext.ComponentQuery.query (giả sử bạn đã mở rộng từ lưới và xác định xtype cho lưới của riêng bạn) Tôi đã trình bày thêm một số nội dung về điều này. Tôi cũng nhận thấy rằng bạn có thể nhấp vào nội dung bằng cách sử dụng xpath để tìm td có chứa giá trị nhận dạng cho một hàng
JonnyRaa

4

Để phát hiện phần tử đó được hiển thị, bạn sử dụng mệnh đề: not(contains(@style, "display: none")

Tốt hơn là sử dụng cái này:

visible_clause = "not(ancestor::*[contains(@style,'display: none')" +
    " or contains(@style, 'visibility: hidden') " + 
    " or contains(@class,'x-hide-display')])"

hidden_clause = "parent::*[contains(@style,'display: none')" + 
    " or contains(@style, 'visibility: hidden')" + 
    " or contains(@class,'x-hide-display')]"

3

Bạn có thể cung cấp thêm thông tin chi tiết về các loại vấn đề bạn đang gặp phải với thử nghiệm extjs không?

Một tiện ích mở rộng Selenium mà tôi thấy hữu ích là waitForCondition . Nếu sự cố của bạn dường như gặp sự cố với các sự kiện Ajax, bạn có thể sử dụng waitForCondition để chờ các sự kiện xảy ra.


3

Các trang web Ext JS có thể khó kiểm tra vì HTML phức tạp mà chúng tạo ra giống như với lưới Ext JS.

Robot HTML5 giải quyết vấn đề này bằng cách sử dụng một loạt các phương pháp hay nhất về cách tra cứu và tương tác một cách đáng tin cậy với các thành phần dựa trên các thuộc tính và điều kiện không động. Sau đó, nó cung cấp các phím tắt để thực hiện việc này với tất cả các thành phần HTML, Ext JS và Sencha Touch mà bạn cần tương tác. Nó có 2 hương vị:

  1. Java - API dựa trên Selenium và JUnit quen thuộc được tích hợp sẵn hỗ trợ trình điều khiển web cho tất cả các trình duyệt hiện đại.
  2. Gwen - Một ngôn ngữ phong cách con người để tạo và duy trì các thử nghiệm trình duyệt một cách nhanh chóng và dễ dàng, đi kèm với môi trường phát triển tích hợp của riêng nó. Tất cả đều dựa trên Java API.

Ví dụ: nếu bạn muốn tìm hàng lưới Ext JS có chứa văn bản "Foo", bạn có thể làm như sau trong Java:

findExtJsGridRow("Foo");

... và bạn có thể làm như sau trong Gwen:

extjsgridrow by text "Foo"

Có rất nhiều tài liệu cho cả Java và Gwen về cách làm việc với các thành phần cụ thể của Ext JS. Tài liệu cũng trình bày chi tiết HTML kết quả cho tất cả các thành phần Ext JS này, mà bạn cũng có thể thấy hữu ích.


2

Các mẹo hữu ích để tìm nạp lưới thông qua Id của lưới trên trang: Tôi nghĩ bạn có thể mở rộng chức năng hữu ích hơn từ API này.

   sub get_grid_row {
        my ($browser, $grid, $row)  = @_;


        my $script = "var doc = this.browserbot.getCurrentWindow().document;\n" .
            "var grid = doc.getElementById('$grid');\n" .
            "var table = grid.getElementsByTagName('table');\n" .
            "var result = '';\n" .
            "var row = 0;\n" . 
            "for (var i = 0; i < table.length; i++) {\n" .
            "   if (table[i].className == 'x-grid3-row-table') {\n".
            "       row++;\n" . 
            "       if (row == $row) {\n" .
            "           var cols_len = table[i].rows[0].cells.length;\n" .
            "           for (var j = 0; j < cols_len; j++) {\n" .
            "               var cell = table[i].rows[0].cells[j];\n" .
            "               if (result.length == 0) {\n" .
            "                   result = getText(cell);\n" .
            "               } else { \n" .
            "                   result += '|' + getText(cell);\n" .
            "               }\n" .
            "           }\n" .
            "       }\n" .
            "   }\n" .
            "}\n" .
            "result;\n";

        my $result = $browser->get_eval($script);
        my @res = split('\|', $result);
        return @res;
    }

2

Kiểm tra dễ dàng hơn thông qua các thuộc tính- dữ liệu HTML tùy chỉnh

Từ tài liệu Sencha :

Một itemId có thể được sử dụng như một cách thay thế để lấy tham chiếu đến một thành phần khi không có tham chiếu đối tượng nào. Thay vì sử dụng id với Ext.getCmp, hãy sử dụng itemId với Ext.container.Container.getComponent sẽ lấy itemId hoặc id. Vì itemId là một chỉ mục cho Bộ sưu tập hỗn hợp bên trong của vùng chứa, itemId được phân bổ cục bộ đến vùng chứa - tránh xung đột tiềm ẩn với Ext.ComponentManager yêu cầu một id duy nhất.

Trọng các Ext.AbstractComponent's onBoxReadyphương pháp, tôi đặt một thuộc tính dữ liệu tùy chỉnh (có tên xuất phát từ tùy chỉnh của tôi testIdAttrtài sản của mỗi thành phần) để của thành phần itemIdgiá trị, nếu nó tồn tại. Thêm Testing.overrides.AbstractComponentlớp vào mảng application.jstệp của bạn requires.

/**
 * Overrides the Ext.AbstracComponent's onBoxReady
 * method to add custom data attributes to the
 * component's dom structure.
 *
 * @author Brian Wendt
 */
Ext.define('Testing.overrides.AbstractComponent', {
  override: 'Ext.AbstractComponent',


  onBoxReady: function () {
    var me = this,
      el = me.getEl();


    if (el && el.dom && me.itemId) {
      el.dom.setAttribute(me.testIdAttr || 'data-selenium-id', me.itemId);
    }


    me.callOverridden(arguments);
  }
});

Phương pháp này cung cấp cho các nhà phát triển một cách để sử dụng lại một số nhận dạng mô tả trong mã của họ và để có những số nhận dạng đó khả dụng mỗi khi trang được hiển thị. Không cần tìm kiếm thông qua các id không mô tả, được tạo động.


2

Chúng tôi đang phát triển một khung thử nghiệm sử dụng selen và đã gặp sự cố với extj (vì đó là kết xuất phía máy khách). Tôi thấy hữu ích khi tìm kiếm một phần tử khi DOM đã sẵn sàng.

public static boolean waitUntilDOMIsReady(WebDriver driver) {
    def maxSeconds = DEFAULT_WAIT_SECONDS * 10
    for (count in 1..maxSeconds) {
        Thread.sleep(100)
        def ready = isDOMReady(driver);
        if (ready) {
            break;
        }
    }
}

public static boolean isDOMReady(WebDriver driver){
    return driver.executeScript("return document.readyState");
}

1

Đối với giao diện người dùng phức tạp không phải là HTML chính thức, xPath luôn là thứ bạn có thể tin tưởng, nhưng hơi phức tạp khi nói đến việc triển khai giao diện người dùng khác nhau bằng cách sử dụng ExtJ.

Bạn có thể sử dụng Firebug và Firexpath làm phần mở rộng của firefox để kiểm tra xpath của một phần tử nhất định và đơn giản chuyển toàn bộ xpath làm tham số cho selen.

Ví dụ trong mã java:

String fullXpath = "xpath=//div[@id='mainDiv']//div[contains(@class,'x-grid-row')]//table/tbody/tr[1]/td[1]//button"

selenium.click(fullXpath);

1

Khi tôi đang thử nghiệm ứng dụng ExtJS bằng WebDriver, tôi đã sử dụng phương pháp tiếp theo: Tôi tìm kiếm trường theo văn bản của nhãn và lấy @forthuộc tính từ nhãn. Ví dụ, chúng tôi có một nhãn

<label id="dynamic_id_label" class="TextboxLabel" for="textField_which_I_am_lloking_for">
Name Of Needed Label
<label/>

Và chúng ta cần phải chỉ WebDriver một số đầu vào: //input[@id=(//label[contains(text(),'Name Of Needed Label')]/@for)].

Vì vậy, nó sẽ chọn id từ @forthuộc tính và sử dụng nó thêm. Đây có lẽ là trường hợp đơn giản nhất nhưng nó cung cấp cho bạn cách xác định vị trí phần tử. Sẽ khó hơn nhiều khi bạn không có nhãn nhưng sau đó bạn cần tìm một số phần tử và viết xpath của mình để tìm kiếm các phần tử anh chị em, các phần tử xuống / lên.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.