unit-testing - ब्राउज़र में extjs कोड का परीक्षण करने के लिए कोई सुझाव, अधिमानतः सेलेनियम के साथ?




selenium web-testing (10)

हम उच्च स्तरीय वेबसाइट परीक्षण (मॉड्यूल स्तर पर व्यापक पायथन सिद्धांतों के अलावा) को संभालने के लिए बड़ी सफलता के साथ सेलेनियम का उपयोग कर रहे हैं। हालांकि अब हम बहुत सारे पृष्ठों के लिए extjs का उपयोग कर रहे हैं और जटिल घटकों जैसे ग्रिड के लिए सेलेनियम परीक्षणों को शामिल करना मुश्किल साबित कर रहे हैं।

क्या किसी ने एक्स्टजेस-आधारित वेब पेजों के लिए स्वचालित परीक्षण लिखने में सफलता प्राप्त की है? बहुत सारे गुगलिंग समान समस्याओं वाले लोगों को पाता है, लेकिन कुछ जवाब। धन्यवाद!


Answers

सेलेनियम के साथ एक्स्टजेस का परीक्षण करने में सबसे बड़ी बाधा यह है कि एक्स्टजेस मानक एचटीएमएल तत्व प्रस्तुत नहीं करता है और सेलेनियम आईडीई नैतिक रूप से (और सही ढंग से) उन तत्वों पर लक्षित आदेश उत्पन्न करेगा जो सजावट के रूप में कार्य करते हैं - अनावश्यक तत्व जो पूरे डेस्कटॉप के साथ एक्स्टजेस की मदद करते हैं- देखो और महसूस। एटीजेएस ऐप के खिलाफ स्वचालित सेलेनियम परीक्षण लिखते समय मैंने कुछ सुझाव और युक्तियां एकत्र की हैं।

सामान्य युक्तियाँ

तत्व ढूंढना

फ़ायरफ़ॉक्स पर सेलेनियम आईडीई के साथ उपयोगकर्ता क्रियाओं को रिकॉर्ड करके सेलेनियम परीक्षण के मामलों को उत्पन्न करते समय, सेलेनियम HTML तत्वों के आईडी पर रिकॉर्ड किए गए कार्यों का आधार रखेगा। हालांकि, अधिकांश क्लिक करने योग्य तत्वों के लिए, ExtJS "ext-gen-345" जैसे उत्पन्न आईडी का उपयोग करता है, जो किसी भी पृष्ठ पर आने वाली विज़िट पर बदलने की संभावना है, भले ही कोई कोड परिवर्तन नहीं किया गया हो। परीक्षण के लिए उपयोगकर्ता क्रियाओं को रिकॉर्ड करने के बाद, जेनरेट की गई आईडी पर निर्भर करने और उन्हें बदलने के लिए ऐसे सभी कार्यों को पार करने के लिए मैन्युअल प्रयास होना आवश्यक है। दो प्रकार के प्रतिस्थापन किए जा सकते हैं जिन्हें बनाया जा सकता है:

एक सीएसएस या XPath लोकेटर के साथ एक आईडी लोकेटर को बदलना

सीएसएस locators "css =" से शुरू होते हैं और XPath locators "//" से शुरू होते हैं ("xpath =" उपसर्ग वैकल्पिक है)। सीएसएस लोकेटर कम वर्बोज़ हैं और पढ़ने के लिए आसान हैं और XPath locators पर प्राथमिकता दी जानी चाहिए। हालांकि, ऐसे मामले हो सकते हैं जहां XPath locators का उपयोग करने की आवश्यकता है क्योंकि एक सीएसएस लोकेटर बस इसे काट नहीं सकता है।

जावास्क्रिप्ट निष्पादित करना

ExtJS द्वारा किए गए जटिल प्रतिपादन के कारण कुछ तत्वों को सरल माउस / कीबोर्ड इंटरैक्शन से अधिक की आवश्यकता होती है। उदाहरण के लिए, एक Ext.form.CombBox वास्तव में एक <select> तत्व नहीं है लेकिन एक अलग ड्रॉप-डाउन सूची वाला टेक्स्ट इनपुट जो दस्तावेज़ पेड़ के नीचे कहीं भी है। कॉम्बोबॉक्स चयन को सही तरीके से अनुकरण करने के लिए, पहले ड्रॉप-डाउन तीर पर क्लिक को अनुकरण करना और फिर दिखाई देने वाली सूची पर क्लिक करना संभव है। हालांकि, इन तत्वों को सीएसएस या एक्सपीएथ लोकेटर के माध्यम से ढूंढना बोझिल हो सकता है। चयन का अनुकरण करने के लिए कॉमबॉक्स घटक स्वयं का पता लगाने और उस पर कॉल करने के तरीकों का एक विकल्प है:

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

सेलेनियम में उपरोक्त ऑपरेशन को अधिक संक्षिप्त रूप में करने के लिए runScript कमांड का उपयोग किया जा सकता है:

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

AJAX और धीमी रेंडरिंग के साथ मुकाबला

सेलेनियम में पृष्ठ लोड के इंतजार के लिए सभी आदेशों के लिए "* AndWait" स्वाद होता है जब उपयोगकर्ता क्रिया पृष्ठ परिवर्तन या पुनः लोड में परिणाम देती है। हालांकि, चूंकि AJAX fetches में वास्तविक पृष्ठ लोड शामिल नहीं हैं, इसलिए इन आदेशों का उपयोग सिंक्रनाइज़ेशन के लिए नहीं किया जा सकता है। समाधान एजेक्स प्रगति संकेतक की उपस्थिति / अनुपस्थिति या ग्रिड, अतिरिक्त घटकों, लिंक इत्यादि में पंक्तियों की उपस्थिति जैसे दृश्य सुरागों का उपयोग करना है उदाहरण के लिए:

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

कभी-कभी एक तत्व कुछ निश्चित समय के बाद ही दिखाई देगा, इस पर निर्भर करता है कि उपयोगकर्ता कार्रवाई के परिणामस्वरूप एक्सटीजेएस घटकों को कितनी तेजी से प्रस्तुत करता है। pause कमांड के साथ मध्यस्थ देरी का उपयोग करने के बजाय, आदर्श विधि तब तक प्रतीक्षा करना है जब तक कि ब्याज का तत्व हमारी समझ में न आए। उदाहरण के लिए, इसे प्रकट होने के इंतजार के बाद किसी आइटम पर क्लिक करने के लिए:

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

मनमाने ढंग से विरामों पर निर्भर करना एक अच्छा विचार नहीं है क्योंकि अलग-अलग ब्राउज़रों या विभिन्न मशीनों पर परीक्षण चलाने से होने वाले समय के मतभेद परीक्षण मामलों को कमजोर कर देंगे।

गैर-क्लिक करने योग्य आइटम

click कमांड द्वारा कुछ तत्व ट्रिगर नहीं किए जा सकते हैं। ऐसा इसलिए है क्योंकि घटना श्रोता वास्तव में कंटेनर पर है, अपने बच्चों के तत्वों पर माउस घटनाओं के लिए देख रहा है, जो अंततः माता-पिता को बुलबुला करता है। टैब नियंत्रण एक उदाहरण है। किसी टैब पर क्लिक करने के लिए, आपको टैब लेबल पर mouseDown ईवेंट अनुकरण करना होगा:

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

फील्ड सत्यापन

प्रपत्र फ़ील्ड्स (Ext.form। * घटक) जो सत्यापन के लिए नियमित अभिव्यक्तियों या vtypes से जुड़े हैं, एक निश्चित विलंब के साथ सत्यापन को ट्रिगर करेंगे ( validationDelay को हटाएं, जो डिफ़ॉल्ट रूप से 250ms पर सेट है), जब उपयोगकर्ता टेक्स्ट दर्ज करता है या तुरंत क्षेत्र में प्रवेश करता है फोकस खो देता है - या ब्लर्स ( validateOnDelay संपत्ति देखें)। किसी क्षेत्र के अंदर कुछ पाठ दर्ज करने के लिए सेलेनियम कमांड को जारी करने के बाद फ़ील्ड सत्यापन को ट्रिगर करने के लिए, आपको निम्न में से कोई एक करना होगा:

  • विलंबित सत्यापन को ट्रिगर करना

    फ़ील्ड कुंजीपटल घटनाओं को प्राप्त करते समय एक्स्टजेस सत्यापन विलंब टाइमर को निकाल देता है। इस टाइमर को ट्रिगर करने के लिए, बस एक डमी कीप इवेंट जारी करें (इससे कोई फ़र्क नहीं पड़ता कि आप जिस कुंजी को एक्सटीजेएस के रूप में उपयोग करते हैं, उसे अनदेखा करता है), उसके बाद एक छोटा विराम होता है जो सत्यापन से अधिक लंबा होता है:

    Command: keyUp
    Target: someTextArea
    Value: x
    Command: pause
    Target: 500
    
  • तत्काल सत्यापन ट्रिगरिंग

    तत्काल सत्यापन को ट्रिगर करने के लिए आप क्षेत्र में धुंधला ईवेंट इंजेक्ट कर सकते हैं:

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

प्रमाणीकरण परिणामों की जांच

सत्यापन के बाद, आप किसी त्रुटि फ़ील्ड की उपस्थिति या अनुपस्थिति की जांच कर सकते हैं:

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

ध्यान दें कि "डिस्प्ले: कोई नहीं" चेक आवश्यक है क्योंकि एक बार त्रुटि फ़ील्ड दिखाया जाता है और फिर इसे छुपाया जाना चाहिए, ExtJS पूरी तरह से इसे DOM पेड़ से हटाने के बजाय त्रुटि फ़ील्ड को छुपाएगा।

तत्व-विशिष्ट युक्तियाँ

Ext.form.Button पर क्लिक करना

  • विकल्प 1

    कमांड: लक्ष्य पर क्लिक करें: css = बटन: इसमें शामिल है ('सहेजें')

    अपने कैप्शन द्वारा बटन का चयन करता है

  • विकल्प 2

    कमांड: लक्ष्य पर क्लिक करें: css = # save-options बटन

    अपने आईडी द्वारा बटन का चयन करता है

Ext.form.ComboBox से एक मान का चयन करना

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

सबसे पहले मान सेट करता है और फिर पर्यवेक्षकों के मामले में स्पष्ट ईवेंट को स्पष्ट रूप से निकाल देता है।


इस blog ने मुझे बहुत मदद की। उन्होंने इस विषय पर काफी कुछ लिखा है और ऐसा लगता है कि यह अभी भी सक्रिय है। लड़का भी अच्छे डिजाइन की सराहना करता है।

वह मूल रूप से प्रश्नों को करने के लिए जावास्क्रिप्ट भेजने और Ext.ComponentQuery.query विधि का उपयोग करके सामान को पुनर्प्राप्त करने के तरीके के बारे में बात करता है जैसे आप अपने ext app में आंतरिक रूप से करते हैं। इस तरह आप xtypes और itemIds का उपयोग कर सकते हैं और किसी पागल ऑटो-जेनरेट की गई सामग्री को पार्स करने की कोशिश करने के बारे में चिंता करने की ज़रूरत नहीं है।

मैंने यह आलेख विशेष रूप से बहुत उपयोगी पाया है।

जल्द ही यहां कुछ और अधिक विस्तृत पोस्ट कर सकते हैं - अभी भी यह ठीक से करने के तरीके के आसपास मेरे सिर को पाने का प्रयास कर रहा है


जब मैं वेबड्राइवर का उपयोग कर ExtJS एप्लिकेशन का परीक्षण कर रहा था, तो मैंने अगले दृष्टिकोण का उपयोग किया: मैंने लेबल के टेक्स्ट द्वारा फ़ील्ड की तलाश की और लेबल से @for विशेषता प्राप्त की। उदाहरण के लिए, हमारे पास एक लेबल है

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

और हमें WebDriver को कुछ इनपुट इंगित करने की आवश्यकता है: //input[@id=(//label[contains(text(),'Name Of Needed Label')]/@for)]

इसलिए, यह आईडी को @for विशेषता से @for और इसे आगे @for । यह शायद सबसे सरल मामला है लेकिन यह आपको तत्व का पता लगाने का तरीका देता है। जब आपके पास कोई लेबल नहीं होता है तो यह बहुत कठिन होता है लेकिन फिर आपको कुछ तत्व ढूंढना होगा और अपने xpath को भाई बहनों की तलाश करना होगा, तत्वों को छोड़ना / चढ़ाना होगा।


हम एक परीक्षण ढांचा विकसित कर रहे हैं जो सेलेनियम का उपयोग करता है और extjs के साथ समस्याओं का सामना करना पड़ता है (क्योंकि यह क्लाइंट साइड प्रतिपादन है)। एक बार डोम तैयार होने के बाद मुझे तत्व की तलाश करना उपयोगी लगता है।

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

यह तत्व पता लगाने के लिए कि आप खंड का उपयोग करते हैं: not(contains(@style, "display: none")

इसका उपयोग करना बेहतर है:

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

मैं सेलेनियम के साथ अपने ExtJs वेब अनुप्रयोग का परीक्षण कर रहा हूं। सबसे बड़ी समस्या में से एक ग्रिड में एक आइटम का चयन करने के लिए कुछ करने के लिए चयन कर रहा था।

इसके लिए, मैंने सहायक विधि लिखी (SeleniumExtJsUtils कक्षा में जो 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)";
}

और जब मुझे एक पंक्ति चुनने की ज़रूरत होती, तो मैं बस फोन करता था:

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

इसके लिए काम करने के लिए मुझे अपनी आईडी को ग्रिड पर सेट करने की आवश्यकता है और एक्स्टजेज़ को स्वयं उत्पन्न नहीं होने दें।


पेज पर ग्रिड के आईडी के माध्यम से ग्रिड लाने के लिए उपयोगी टिप्स: मुझे लगता है कि आप इस एपीआई से अधिक उपयोगी फ़ंक्शन का विस्तार कर सकते हैं।

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

जटिल यूआई के लिए जो औपचारिक एचटीएमएल नहीं है, एक्सपैथ हमेशा कुछ है जिस पर आप भरोसा कर सकते हैं, लेकिन जब एक्सटीजे का उपयोग करके विभिन्न यूआई कार्यान्वयन की बात आती है तो थोड़ा जटिल होता है।

आप किसी निश्चित तत्व के xpath का परीक्षण करने के लिए फ़ायरफ़ॉक्स एक्सटेंशन के रूप में फ़ायरफ़ॉक्स और फ़ायरक्सपाथ का उपयोग कर सकते हैं, और सेलेनियम के पैरामीटर के रूप में पूर्ण xpath को सरल पास कर सकते हैं।

उदाहरण के लिए जावा कोड में:

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

selenium.click(fullXpath);

क्या आप extjs परीक्षण के साथ होने वाली समस्याओं के प्रकारों में अधिक अंतर्दृष्टि प्रदान कर सकते हैं?

एक सेलेनियम एक्सटेंशन मुझे उपयोगी लगता है waitForCondition । अगर आपकी समस्या अजाक्स घटनाओं में परेशानी प्रतीत होती है, तो आप घटनाओं के इंतजार के लिए प्रतीक्षा करने के लिए प्रतीक्षा करें।


किसी अन्य परिप्रेक्ष्य से कवरेज देखना: नियंत्रण के स्पष्ट प्रवाह के साथ अच्छी तरह से लिखित कोड कवर करना सबसे आसान है, पढ़ने के लिए सबसे आसान है, और आमतौर पर कम से कम छोटी गाड़ी कोड। स्पष्टता और कवर योग्यता के साथ कोड लिखकर, और कोड के साथ समानांतर में यूनिट परीक्षण लिखकर, आपको सर्वोत्तम परिणाम IMHO मिलते हैं।





unit-testing extjs selenium web-testing