Adakah saran untuk menguji kode extjs di browser, lebih disukai dengan selenium?

92

Kami telah menggunakan selenium dengan sukses besar untuk menangani pengujian situs web tingkat tinggi (selain doctests python ekstensif di tingkat modul). Namun sekarang kami menggunakan extjs untuk banyak halaman dan terbukti sulit untuk menggabungkan tes Selenium untuk komponen kompleks seperti grid.

Adakah yang berhasil menulis tes otomatis untuk halaman web berbasis extjs? Banyak googling menemukan orang dengan masalah serupa, tetapi sedikit jawaban. Terima kasih!

mm2001
sumber
Orang-orang baik di Ext JS telah berbaik hati untuk memposting tentang subjek ini di blog mereka di sini . Saya harap ini membantu.
NBRed5

Jawaban:

173

Rintangan terbesar dalam menguji ExtJS dengan Selenium adalah bahwa ExtJS tidak merender elemen HTML standar dan Selenium IDE akan secara naif (dan berhak) menghasilkan perintah yang ditargetkan pada elemen yang hanya bertindak sebagai dekorasi - elemen berlebihan yang membantu ExtJS dengan seluruh desktop- Lihat dan rasakan. Berikut adalah beberapa tip dan trik yang saya kumpulkan saat menulis tes Selenium otomatis terhadap aplikasi ExtJS.

Tip Umum

Menemukan Elemen

Saat membuat kasus uji Selenium dengan merekam tindakan pengguna dengan Selenium IDE di Firefox, Selenium akan mendasarkan tindakan yang direkam pada id elemen HTML. Namun, untuk sebagian besar elemen yang dapat diklik, ExtJS menggunakan id yang dihasilkan seperti "ext-gen-345" yang kemungkinan besar akan berubah pada kunjungan berikutnya ke halaman yang sama, meskipun tidak ada perubahan kode yang dilakukan. Setelah merekam tindakan pengguna untuk sebuah pengujian, perlu ada upaya manual untuk melakukan semua tindakan yang bergantung pada id yang dihasilkan dan menggantinya. Ada dua jenis penggantian yang bisa dilakukan:

Mengganti Id Locator dengan CSS atau XPath Locator

Pencari CSS dimulai dengan "css =" dan pelacak XPath dimulai dengan "//" (awalan "xpath =" adalah opsional). Pelacak CSS tidak terlalu bertele-tele dan lebih mudah dibaca dan lebih disukai daripada pencari XPath. Namun, mungkin ada kasus di mana pelacak XPath perlu digunakan karena pelacak CSS tidak dapat memotongnya.

Menjalankan JavaScript

Beberapa elemen memerlukan lebih dari sekadar interaksi mouse / keyboard karena rendering kompleks yang dilakukan oleh ExtJS. Misalnya, Ext.form.CombBox sebenarnya bukan <select>elemen tetapi input teks dengan daftar drop-down terpisah yang ada di suatu tempat di bagian bawah pohon dokumen. Untuk mensimulasikan pilihan ComboBox dengan benar, pertama-tama mungkin untuk mensimulasikan klik pada panah drop-down dan kemudian mengklik daftar yang muncul. Namun, menemukan elemen ini melalui pelacak CSS atau XPath bisa jadi merepotkan. Alternatifnya adalah dengan mencari komponen ComoBox itu sendiri dan memanggil metode di atasnya untuk mensimulasikan pemilihan:

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

Di Selenium, runScriptperintah dapat digunakan untuk melakukan operasi di atas dalam bentuk yang lebih ringkas:

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

Mengatasi AJAX dan Slow Rendering

Selenium memiliki rasa "* AndWait" untuk semua perintah untuk menunggu pemuatan halaman saat tindakan pengguna menghasilkan transisi halaman atau memuat ulang. Namun, karena pengambilan AJAX tidak melibatkan pemuatan halaman yang sebenarnya, perintah ini tidak dapat digunakan untuk sinkronisasi. Solusinya adalah dengan memanfaatkan petunjuk visual seperti ada / tidak adanya indikator kemajuan AJAX atau munculnya baris dalam kisi, komponen tambahan, tautan, dll. Misalnya:

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

Terkadang sebuah elemen hanya akan muncul setelah jangka waktu tertentu, bergantung pada seberapa cepat ExtJS merender komponen setelah tindakan pengguna menghasilkan perubahan tampilan. Alih-alih menggunakan penundaan arbiter dengan pauseperintah, metode yang ideal adalah menunggu sampai elemen yang menarik datang dalam genggaman kita. Misalnya, untuk mengklik suatu item setelah menunggu item itu muncul:

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

Mengandalkan jeda sewenang-wenang bukanlah ide yang baik karena perbedaan waktu yang dihasilkan dari menjalankan pengujian di browser yang berbeda atau pada mesin yang berbeda akan membuat kasus pengujian tidak stabil.

Item yang tidak dapat diklik

Beberapa elemen tidak dapat dipicu oleh clickperintah. Itu karena event listener sebenarnya ada di container, mengamati kejadian mouse pada elemen anaknya, yang akhirnya mengarah ke induknya. Kontrol tab adalah salah satu contohnya. Untuk mengklik tab, Anda harus mensimulasikan mouseDownacara di label tab:

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

Validasi Bidang

Bidang formulir (komponen Ext.form. *) Yang memiliki ekspresi reguler atau vtypes terkait untuk validasi akan memicu validasi dengan penundaan tertentu (lihat validationDelayproperti yang disetel ke 250 md secara default), setelah pengguna memasukkan teks atau segera ketika bidang kehilangan fokus - atau kabur (lihat validateOnDelayproperti). Untuk memicu validasi kolom setelah mengeluarkan perintah jenis Selenium untuk memasukkan beberapa teks di dalam kolom, Anda harus melakukan salah satu hal berikut:

  • Memicu Validasi Tertunda

    ExtJS mengaktifkan timer tunda validasi saat bidang menerima peristiwa keyup. Untuk memicu pengatur waktu ini, cukup keluarkan acara dummy keyup (tidak masalah kunci mana yang Anda gunakan karena ExtJS mengabaikannya), diikuti dengan jeda singkat yang lebih lama dari validationDelay:

    Command: keyUp
    Target: someTextArea
    Value: x
    Command: pause
    Target: 500
  • Memicu Validasi Segera

    Anda dapat memasukkan peristiwa buram ke dalam bidang untuk memicu validasi langsung:

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

Memeriksa Hasil Validasi

Setelah validasi, Anda dapat memeriksa ada atau tidaknya bidang kesalahan:

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"))]

Perhatikan bahwa pemeriksaan "display: none" diperlukan karena setelah bidang kesalahan ditampilkan dan kemudian harus disembunyikan, ExtJS hanya akan menyembunyikan bidang kesalahan alih-alih menghapusnya sepenuhnya dari pohon DOM.

Kiat khusus elemen

Mengklik sebuah Ext.form.Button

  • Pilihan 1

    Perintah: klik Target: css = tombol: berisi ('Simpan')

    Memilih tombol dengan keterangannya

  • pilihan 2

    Perintah: klik Target: css = # tombol simpan-opsi

    Memilih tombol berdasarkan id-nya

Memilih Nilai dari Ext.form.ComboBox

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

Pertama-tama setel nilainya dan kemudian secara eksplisit mengaktifkan peristiwa pemilihan jika ada pengamat.

Ates Goral
sumber
3
Berikut adalah beberapa tip manual: rkapse.blogspot.nl/2009/06/selenium-ide-issues-with-extjs.html
dan-klasson
5

Blog ini membantu saya. Dia menulis cukup banyak tentang topik ini dan sepertinya masih aktif. Pria itu juga tampaknya menghargai desain yang bagus.

Dia pada dasarnya berbicara tentang menggunakan pengiriman javascript untuk melakukan kueri dan menggunakan metode Ext.ComponentQuery.query untuk mengambil barang dengan cara yang sama seperti yang Anda lakukan di aplikasi ext Anda secara internal. Dengan cara itu Anda dapat menggunakan xtypes dan itemIds dan tidak perlu khawatir tentang mencoba mengurai salah satu item gila yang dibuat secara otomatis.

Saya menemukan artikel ini sangat membantu.

Mungkin akan memposting sesuatu yang sedikit lebih detail di sini segera - masih mencoba memikirkan cara melakukan ini dengan benar

JonnyRaa
sumber
4

Saya telah menguji aplikasi web ExtJs saya dengan selenium. Salah satu masalah terbesar adalah memilih item di grid untuk melakukan sesuatu dengannya.

Untuk ini, saya menulis metode pembantu (di kelas SeleniumExtJsUtils yang merupakan kumpulan metode yang berguna untuk interaksi yang lebih mudah dengan 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)";
}

dan ketika saya perlu memilih baris, saya cukup menelepon:

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

Agar ini berfungsi, saya perlu mengatur id saya di grid dan tidak membiarkan ExtJ menghasilkannya sendiri.

Marko Dumic
sumber
Anda harus dapat menemukan grid dengan menggunakan xtype-nya di Ext.ComponentQuery.query (dengan asumsi Anda telah memperluas dari grid dan mendefinisikan xtype untuk grid Anda sendiri) Saya telah meletakkan beberapa hal tentang ini lebih jauh. Saya juga menemukan bahwa Anda dapat mengklik sesuatu dengan menggunakan xpath untuk menemukan td yang berisi nilai pengenal untuk baris
JonnyRaa
4

Untuk mendeteksi elemen tersebut terlihat Anda menggunakan klausa: not(contains(@style, "display: none")

Lebih baik menggunakan ini:

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')]"
Master-Test
sumber
3

Bisakah Anda memberikan lebih banyak wawasan tentang jenis masalah yang Anda alami dengan pengujian extjs?

Satu ekstensi Selenium yang menurut saya berguna adalah waitForCondition . Jika masalah Anda tampaknya adalah masalah dengan peristiwa Ajax, Anda dapat menggunakan waitForCondition untuk menunggu peristiwa terjadi.

Ryan Tamu
sumber
3

Halaman web Ext JS bisa jadi rumit untuk diuji, karena HTML rumit yang mereka hasilkan seperti dengan kisi Ext JS.

Robot HTML5 menangani hal ini dengan menggunakan serangkaian praktik terbaik tentang cara andal mencari dan berinteraksi dengan komponen berdasarkan atribut dan kondisi yang tidak dinamis. Kemudian menyediakan pintasan untuk melakukan ini dengan semua komponen HTML, Ext JS, dan Sencha Touch yang Anda perlukan untuk berinteraksi. Itu datang dalam 2 rasa:

  1. Jawa - API berbasis Selenium dan JUnit yang sudah dikenal yang memiliki dukungan driver web bawaan untuk semua browser modern.
  2. Gwen - Bahasa gaya manusia untuk membuat dan memelihara pengujian browser dengan cepat dan mudah, yang hadir dengan lingkungan pengembangan terintegrasi sendiri. Semuanya didasarkan pada Java API.

Misalnya jika Anda ingin menemukan baris kisi Ext JS yang berisi teks "Foo", Anda dapat melakukan hal berikut di Java:

findExtJsGridRow("Foo");

... dan Anda dapat melakukan hal berikut di Gwen:

extjsgridrow by text "Foo"

Ada banyak dokumentasi untuk Java dan Gwen tentang cara bekerja dengan komponen khusus Ext JS. Dokumentasi juga merinci HTML yang dihasilkan untuk semua komponen Ext JS ini, yang mungkin berguna bagi Anda.

John V
sumber
2

Tip berguna untuk mengambil grid melalui Id grid pada halaman: Saya rasa Anda bisa memperluas fungsi yang lebih berguna dari API ini.

   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;
    }

sumber
2

Pengujian yang lebih mudah melalui atribut data- HTML khusus

Dari dokumentasi Sencha :

ItemId dapat digunakan sebagai cara alternatif untuk mendapatkan referensi ke sebuah komponen jika tidak ada referensi objek yang tersedia. Sebagai ganti menggunakan id dengan Ext.getCmp, gunakan itemId dengan Ext.container.Container.getComponent yang akan mengambil itemId atau id. Karena itemId adalah indeks untuk MixedCollection internal penampung, itemId dicakup secara lokal ke penampung - menghindari potensi konflik dengan Ext.ComponentManager yang memerlukan id unik.

Mengganti metode Ext.AbstractComponent's onBoxReady, saya menetapkan atribut data khusus (yang namanya berasal dari testIdAttrproperti khusus saya setiap komponen) ke nilai komponen itemId, jika ada. Tambahkan Testing.overrides.AbstractComponentkelas ke array application.jsfile Anda 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);
  }
});

Metode ini memberi pengembang cara untuk menggunakan kembali pengenal deskriptif dalam kode mereka dan agar pengenal tersebut tersedia setiap kali halaman dirender. Tidak perlu lagi menelusuri melalui id non-deskriptif yang dibuat secara dinamis.

Brian Wendt
sumber
2

Kami sedang mengembangkan kerangka pengujian yang menggunakan selenium dan mengalami masalah dengan extjs (karena itu rendering sisi klien). Saya merasa berguna untuk mencari elemen setelah DOM siap.

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");
}
bertanasco
sumber
1

Untuk UI kompleks yang bukan HTML formal, xPath selalu merupakan sesuatu yang dapat Anda andalkan, tetapi sedikit rumit dalam hal implementasi UI yang berbeda menggunakan ExtJ.

Anda dapat menggunakan Firebug dan Firexpath sebagai ekstensi firefox untuk menguji xpath elemen tertentu, dan cukup lewati full xpath sebagai parameter ke selenium.

Misalnya dalam kode java:

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

selenium.click(fullXpath);
Chun
sumber
1

Ketika saya menguji aplikasi ExtJS menggunakan WebDriver saya menggunakan pendekatan selanjutnya: Saya mencari kolom dengan teks label dan mendapatkan @foratribut dari label. Misalnya, kami memiliki label

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

Dan kita perlu menunjukkan WebDriver beberapa masukan: //input[@id=(//label[contains(text(),'Name Of Needed Label')]/@for)].

Jadi, itu akan memilih id dari @foratribut dan menggunakannya lebih jauh. Ini mungkin kasus paling sederhana tetapi memberi Anda cara untuk menemukan elemen. Ini jauh lebih sulit ketika Anda tidak memiliki label tetapi kemudian Anda perlu menemukan beberapa elemen dan menulis xpath Anda untuk mencari siblings, descend / ascend elemen.

olyv
sumber