एस्प्रेसो: थ्रेड.स्लीप ();


102

एस्प्रेसो का दावा है कि इसके लिए कोई आवश्यकता नहीं है Thread.sleep();, लेकिन मेरा कोड तब तक काम नहीं करता जब तक कि मैं इसे शामिल नहीं करता। मैं एक आईपी से जुड़ रहा हूं। कनेक्ट करते समय, एक प्रगति संवाद दिखाया जाता है। sleepसंवाद ख़ारिज करने के लिए मुझे प्रतीक्षा करने की आवश्यकता है । यह मेरा परीक्षण स्निपेट है जहां मैं इसका उपयोग करता हूं:

    IP.enterIP(); // fills out an IP dialog (this is done with espresso)

    //progress dialog is now shown
    Thread.sleep(1500);

    onView(withId(R.id.button).perform(click());

मैंने इस कोड के साथ और इसके बिना कोशिश की है , Thread.sleep();लेकिन यह कहता R.id.Buttonहै कि इसका कोई अस्तित्व नहीं है। मुझे काम करने का एकमात्र तरीका नींद के साथ है।

इसके अलावा, मैंने Thread.sleep();चीजों को बदलने की कोशिश की है जैसे कि getInstrumentation().waitForIdleSync();और फिर भी किस्मत नहीं।

क्या ऐसा करने का एकमात्र तरीका है? या क्या मैं कुछ न कुछ भूल रहा हूं?

अग्रिम में धन्यवाद।


क्या आपके लिए यह संभव है कि आप अनचाहे लूप लगाएं, वैसे भी आप कॉल को ब्लॉक करना चाहते हैं।
केदारक २ k

ठीक है .. मुझे समझाने की। आपके लिए 2 सुझाव 1) कॉल-बैक तरह के तंत्र को लागू करें। ऑन-कनेक्शन-स्थापित कॉल एक विधि और दृश्य दिखाओ। 2) आप IP.enterIP () के बीच विलंब बनाना चाहते हैं; और onView (....) तो आप लूप डाल सकते हैं जो सिम्यूलर किस्म के कॉल को ऑनव्यू (..) करने में देरी करेगा ... लेकिन मुझे लगता है कि यदि संभव हो तो कृपया विकल्प नंबर 1 पसंद करें (कॉल-बैक बनाना) तंत्र) ...
केदारक

@kedark हाँ यह एक विकल्प है, लेकिन क्या यह एस्प्रेसो का समाधान है?
चाड बिंघम

आपके प्रश्न में अनुत्तरित टिप्पणियाँ हैं, क्या आप उनका उत्तर दे सकते हैं?
बोल्शोस

@ बोल्हो, क्या सवाल?
चाड बिंगहैम

जवाबों:


111

मेरे दिमाग में सही दृष्टिकोण होगा:

/** Perform action of waiting for a specific view id. */
public static ViewAction waitId(final int viewId, final long millis) {
    return new ViewAction() {
        @Override
        public Matcher<View> getConstraints() {
            return isRoot();
        }

        @Override
        public String getDescription() {
            return "wait for a specific view with id <" + viewId + "> during " + millis + " millis.";
        }

        @Override
        public void perform(final UiController uiController, final View view) {
            uiController.loopMainThreadUntilIdle();
            final long startTime = System.currentTimeMillis();
            final long endTime = startTime + millis;
            final Matcher<View> viewMatcher = withId(viewId);

            do {
                for (View child : TreeIterables.breadthFirstViewTraversal(view)) {
                    // found view with required ID
                    if (viewMatcher.matches(child)) {
                        return;
                    }
                }

                uiController.loopMainThreadForAtLeast(50);
            }
            while (System.currentTimeMillis() < endTime);

            // timeout happens
            throw new PerformException.Builder()
                    .withActionDescription(this.getDescription())
                    .withViewDescription(HumanReadables.describe(view))
                    .withCause(new TimeoutException())
                    .build();
        }
    };
}

और तब उपयोग का पैटर्न होगा:

// wait during 15 seconds for a view
onView(isRoot()).perform(waitId(R.id.dialogEditor, TimeUnit.SECONDS.toMillis(15)));

3
धन्यवाद एलेक्स, आपने IdlingResource या AsyncTasks पर यह विकल्प क्यों चुना?
टिम बोलैंड

1
यह वर्कअराउंड दृष्टिकोण है, ज्यादातर मामलों में एस्प्रेसो बिना किसी समस्या के काम कर रहा है और विशेष 'प्रतीक्षा कोड'। मैं वास्तव में कई अलग-अलग तरीकों की कोशिश करता हूं, और सोचता हूं कि यह सबसे मेल खाता एस्प्रेसो वास्तुकला / डिजाइन है।
ओलेक्सांद्र कुचेन्को

1
@AlexK यह मेरे दिन दोस्त बना दिया!
dawid gdanski

1
मेरे लिए, यह एपीआई के लिए विफल रहता है <= 19, लाइन पर नई PerformException.Builder () फेंक
प्रबीन तिमिना

4
मुझे उम्मीद है कि आप समझ गए होंगे कि इसका एक नमूना, आप अपनी आवश्यकताओं के लिए कॉपी / पेस्ट और संशोधित कर सकते हैं। इसकी पूरी जिम्मेदारी आपकी खुद की व्यावसायिक जरूरतों में उपयोग करने की है, न कि मेरी।
ओलेक्ज़ेंडर कुचेन्को 14

47

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

/**
 * Perform action of waiting for a specific time.
 */
public static ViewAction waitFor(final long millis) {
    return new ViewAction() {
        @Override
        public Matcher<View> getConstraints() {
            return isRoot();
        }

        @Override
        public String getDescription() {
            return "Wait for " + millis + " milliseconds.";
        }

        @Override
        public void perform(UiController uiController, final View view) {
            uiController.loopMainThreadForAtLeast(millis);
        }
    };
}

इसलिए आप Delayइसे आसानी से एक्सेस करने के लिए एक क्लास बना सकते हैं और उसमें यह तरीका डाल सकते हैं। आप इसे अपने टेस्ट क्लास में उसी तरह से उपयोग कर सकते हैं:onView(isRoot()).perform(waitFor(5000));


7
प्रदर्शन विधि को इस तरह एक पंक्ति के साथ भी सरल किया जा सकता है: uiController.loopMainThreadForAtLeast (मिलिस);
यैर कुकिल्का १ '

बहुत बढ़िया, मुझे लगता है कि पता नहीं था: @YairKukielka THUMBS_UP
Hesam

व्यस्त प्रतीक्षा के लिए बाइक।
ट्वीस्टेरोब

बहुत बढ़िया। मैं उस युग के लिए खोज रहा था। प्रतीक्षा समस्याओं के लिए एक सरल समाधान के लिए +1।
टोबियास रीच

उपयोग करने के बजाय देरी को जोड़ने का बहुत बेहतर तरीकाThread.sleep()
वाहिब उल हक

23

जब मैं एक सर्वर प्रतिक्रिया की प्रतीक्षा कर रहा था और प्रतिक्रिया के आधार पर तत्वों की दृश्यता बदलने की प्रतीक्षा कर रहा था, तो मैं इस धागे पर ठोकर खाई।

जब भी ऊपर दिए गए समाधान में निश्चित रूप से मदद मिली, मैंने अंततः चिउकी से इस उत्कृष्ट उदाहरण को पाया और अब उस दृष्टिकोण का उपयोग अपने गो-टू के रूप में करता हूं जब भी मैं ऐप निष्क्रिय अवधि के दौरान होने वाली क्रियाओं की प्रतीक्षा कर रहा हूं।

मैंने अपनी उपयोगिताओं वर्ग में ElapsedTimeIdlingResource () जोड़ा है , अब प्रभावी रूप से एस्प्रेसो-उचित विकल्प के रूप में उपयोग कर सकता है, और अब उपयोग अच्छा और साफ है:

// Make sure Espresso does not time out
IdlingPolicies.setMasterPolicyTimeout(waitingTime * 2, TimeUnit.MILLISECONDS);
IdlingPolicies.setIdlingResourceTimeout(waitingTime * 2, TimeUnit.MILLISECONDS);

// Now we wait
IdlingResource idlingResource = new ElapsedTimeIdlingResource(waitingTime);
Espresso.registerIdlingResources(idlingResource);

// Stop and verify
onView(withId(R.id.toggle_button))
    .check(matches(withText(R.string.stop)))
    .perform(click());
onView(withId(R.id.result))
    .check(matches(withText(success ? R.string.success: R.string.failure)));

// Clean up
Espresso.unregisterIdlingResources(idlingResource);

मुझे एक I/TestRunner: java.lang.NoClassDefFoundError: fr.x.app.y.testtools.ElapsedTimeIdlingResourceत्रुटि मिलती है । कोई उपाय। मैं Proguard का उपयोग करता हूं, लेकिन अक्षम ऑबफ्यूजन के साथ।
एंथनी

-keepउन वर्गों के लिए एक बयान जोड़ने की कोशिश करें जो यह सुनिश्चित करने के लिए नहीं मिल रहे हैं कि प्रोगार्ड उन्हें अनावश्यक के रूप में नहीं निकाल रहा है। अधिक जानकारी यहाँ: developer.android.com/tools/help/proguard.html#keep-code
मैटमैट

मैं एक प्रश्न stackoverflow.com/questions/36859528/… पोस्ट करता हूं । वर्ग में बीज है। txt और mapping.txt
एंथोनी

2
यदि आपको निष्क्रिय नीतियों को बदलने की आवश्यकता है, तो आप शायद निष्क्रिय संसाधनों को सही तरीके से लागू नहीं कर रहे हैं। लंबे समय में यह तय करने में समय लगाने के लिए बेहतर है। इस पद्धति से अंततः धीमी और परतदार परीक्षाएं होंगी। की जाँच करें google.github.io/android-testing-support-library/docs/espresso/...
जोस Alcérreca

आप बिल्कुल सही हैं। यह उत्तर एक वर्ष से अधिक पुराना है, और तब से बेकार संसाधनों के व्यवहार में सुधार हुआ है, वही उपयोग मामला जो मैंने अभी के लिए उपरोक्त कोड का उपयोग किया है वह बॉक्स से बाहर काम करता है, ठीक से नकली एपीआई क्लाइंट का पता लगाता है - हम अब उपरोक्त का उपयोग नहीं करते हैं उस कारण के लिए हमारे इंस्ट्रूमेंटेड परीक्षणों में ElapsedTimeIdlingResource। (आप निश्चित रूप से सभी चीजों को आरएक्स कर सकते हैं, जो प्रतीक्षा अवधि में हैक करने की आवश्यकता को नकारती है)। यह कहा गया है, चीजों को करने का Google तरीका हमेशा सबसे अच्छा नहीं होता है: दार्शनिकहैकर . com / post/…
मैटमैट

18

मुझे लगता है कि इस पंक्ति को जोड़ना अधिक आसान है:

SystemClock.sleep(1500);

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


एक्सप्रेसो इन हार्डकोड नींद से बचने के लिए है जो परतदार परीक्षणों का कारण बनता है। अगर यह मामला है, तो मैं अपीमी जैसे ब्लैकबॉक्स टूल के लिए जा सकता हूं
Emjey

6

आप बस बरिस्ता विधियों का उपयोग कर सकते हैं:

BaristaSleepActions.sleep(2000);

BaristaSleepActions.sleep(2, SECONDS);

बरिस्ता एक पुस्तकालय है जो स्वीकार किए गए उत्तर द्वारा आवश्यक सभी कोड को जोड़ने से बचने के लिए एस्प्रेसो को लपेटता है। और यहाँ एक लिंक है! https://github.com/SchibstedSpain/Barista


मुझे इसमें फर्क नहीं है और सिर्फ एक थ्रेड स्लीप करके
पाब्लो कैविगलिया

ईमानदारी से, मुझे याद नहीं है कि Google के किस वीडियो में एक आदमी ने कहा था कि हमें इस तरह से एक आम बनाने के बजाय नींद का उपयोग करना चाहिए Thread.sleep()। माफ़ करना! यह एस्प्रेसो के बारे में Google द्वारा बनाए गए कुछ पहले वीडियो में था, लेकिन मुझे याद नहीं है कि कौन सा ... यह कुछ साल पहले था। माफ़ करना! : ·) ओह! संपादित करें! मैंने तीन साल पहले खोले गए पीआर में वीडियो का लिंक डाला। इसकी जांच - पड़ताल करें! github.com/AdevintaSpain/Barista/pull/19
Roc Boronat

5

यह इस उत्तर के समान है, लेकिन प्रयासों के बजाय एक टाइमआउट का उपयोग करता है और अन्य ViewInteractions के साथ जंजीर किया जा सकता है:

/**
 * Wait for view to be visible
 */
fun ViewInteraction.waitUntilVisible(timeout: Long): ViewInteraction {
    val startTime = System.currentTimeMillis()
    val endTime = startTime + timeout

    do {
        try {
            check(matches(isDisplayed()))
            return this
        } catch (e: NoMatchingViewException) {
            Thread.sleep(50)
        }
    } while (System.currentTimeMillis() < endTime)

    throw TimeoutException()
}

उपयोग:

onView(withId(R.id.whatever))
    .waitUntilVisible(5000)
    .perform(click())

4

मैं कोडिंग और एस्प्रेसो के लिए नया हूं, इसलिए जब मैं जानता हूं कि अच्छा और उचित समाधान सुस्ती का उपयोग करना है, तो मैं अभी तक ऐसा करने में बुद्धिमान नहीं हूं।

जब तक मैं अधिक जानकार नहीं हो जाता, तब भी मुझे अपने परीक्षणों को किसी तरह चलाने की आवश्यकता होती है, इसलिए अभी के लिए मैं इस गंदे समाधान का उपयोग कर रहा हूं, जो एक तत्व को खोजने में कई प्रयास करता है, अगर यह मिल जाता है तो रुक जाता है और यदि नहीं, तो संक्षेप में सोता है और शुरू होता है जब तक यह प्रयासों की अधिकतम एनआर तक नहीं पहुंचता (अब तक के प्रयासों की अधिकतम संख्या लगभग 150 रही है)।

private static boolean waitForElementUntilDisplayed(ViewInteraction element) {
    int i = 0;
    while (i++ < ATTEMPTS) {
        try {
            element.check(matches(isDisplayed()));
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            try {
                Thread.sleep(WAITING_TIME);
            } catch (Exception e1) {
                e.printStackTrace();
            }
        }
    }
    return false;
}

मैं उन सभी तरीकों का उपयोग कर रहा हूं जो आईडी, पाठ, माता-पिता आदि द्वारा तत्व ढूंढ रहे हैं:

static ViewInteraction findById(int itemId) {
    ViewInteraction element = onView(withId(itemId));
    waitForElementUntilDisplayed(element);
    return element;
}

आपके उदाहरण में, findById(int itemId)विधि एक तत्व (जो NULL हो सकता है) waitForElementUntilDisplayed(element);लौटाएगा चाहे रिटर्न सही हो या गलत .... इसलिए, यह ठीक नहीं है
mbob

बस में झंकार करना चाहता था और कहता था कि यह मेरी राय में सबसे अच्छा समाधान है। IdlingResource5-सेकंड पोलिंग रेट ग्रैन्युलैरिटी (मेरे उपयोग के मामले के लिए बहुत बड़ा तरीका) के कारण मेरे लिए पर्याप्त नहीं है। स्वीकृत उत्तर मेरे लिए या तो काम नहीं करता (इस उत्तर की लंबी टिप्पणी फ़ीड में पहले से ही शामिल क्यों है इसका स्पष्टीकरण)। इसके लिए धन्यवाद! मैंने आपका विचार लिया और अपना समाधान बनाया और यह एक आकर्षण की तरह काम करता है।
oaskamay

हां, यह एकमात्र समाधान है जो मेरे लिए भी काम करता है, जब मैं उन तत्वों की प्रतीक्षा करना चाहता हूं जो वर्तमान गतिविधि में नहीं हैं।
गिलहरमेकरज़

3

एस्प्रेसो परीक्षणों में नींद () कॉल से बचने के लिए बनाया गया है। आपके परीक्षण को आईपी दर्ज करने के लिए एक संवाद नहीं खोलना चाहिए, जो कि परीक्षणित गतिविधि की जिम्मेदारी होनी चाहिए।

दूसरी ओर, आपका UI परीक्षण होना चाहिए:

  • IP संवाद के प्रकट होने की प्रतीक्षा करें
  • IP एड्रेस भरें और एंटर पर क्लिक करें
  • आपके बटन के प्रकट होने और उसे क्लिक करने की प्रतीक्षा करें

परीक्षा कुछ इस तरह दिखनी चाहिए:

// type the IP and press OK
onView (withId (R.id.dialog_ip_edit_text))
  .check (matches(isDisplayed()))
  .perform (typeText("IP-TO-BE-TYPED"));

onView (withText (R.string.dialog_ok_button_title))
  .check (matches(isDisplayed()))
  .perform (click());

// now, wait for the button and click it
onView (withId (R.id.button))
  .check (matches(isDisplayed()))
  .perform (click());

एस्प्रेसो उन सभी चीजों की प्रतीक्षा करता है जो आपके परीक्षण को निष्पादित करने से पहले UI थ्रेड और AsyncTask पूल दोनों में हो रही हैं।

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


2
आपका उदाहरण कोड अनिवार्य रूप से वही कोड है जो मैंने अपने प्रश्न में लिखा है।
चाड बिंघम

@Binghammer मेरा मतलब है कि परीक्षा का व्यवहार ऐसा होना चाहिए जैसे उपयोगकर्ता व्यवहार करता है। हो सकता है कि जो बिंदु मुझे याद आ रहा है वह वही हो जो आपका IP.enterIP () विधि करता है। क्या आप अपना प्रश्न संपादित कर सकते हैं और स्पष्ट कर सकते हैं?
बोलहोसो

मेरी टिप्पणियाँ कहती हैं कि यह क्या करता है। यह एस्प्रेसो में सिर्फ एक विधि है जो आईपी संवाद को भरता है। यह सब यूआई है।
चाड बिंघम

मिमी ... ठीक है, तो आप सही हैं, मेरा परीक्षण मूल रूप से वही कर रहा है। क्या आप UI थ्रेड या AsyncTasks से कुछ करते हैं?
बोल्शोसो

16
एस्प्रेसो इस उत्तर के कोड और पाठ की तरह काम नहीं करता है। ViewInteraction पर एक चेक कॉल तब तक प्रतीक्षा नहीं करेगा जब तक कि दिया गया माचिस सफल नहीं हो जाता है, लेकिन स्थिति पूरी न होने पर तुरंत विफल हो जाते हैं। ऐसा करने का सही तरीका यह है कि या तो AsyncTasks का उपयोग किया जाए, जैसा कि इस उत्तर में वर्णित है, या, यदि किसी तरह संभव नहीं है, तो एक IdlingResource लागू करें जो परीक्षण के निष्पादन के साथ आगे बढ़ने के लिए एस्प्रेसो के UiController को सूचित करेगा।
हैफैक्स

2

आपको एस्प्रेसो आइडलिंग रिसोर्स का उपयोग करना चाहिए, यह इस कोडलैब में सुझाया गया है

एक निष्क्रिय संसाधन एक अतुल्यकालिक ऑपरेशन का प्रतिनिधित्व करता है जिसके परिणाम UI परीक्षण में बाद के संचालन को प्रभावित करते हैं। एस्प्रेसो के साथ निष्क्रिय संसाधनों को पंजीकृत करके, आप अपने ऐप का परीक्षण करते समय इन अतुल्यकालिक संचालन को अधिक मज़बूती से सत्यापित कर सकते हैं।

प्रस्तुतकर्ता से एक अतुल्यकालिक कॉल का उदाहरण

@Override
public void loadNotes(boolean forceUpdate) {
   mNotesView.setProgressIndicator(true);
   if (forceUpdate) {
       mNotesRepository.refreshData();
   }

   // The network request might be handled in a different thread so make sure Espresso knows
   // that the app is busy until the response is handled.
   EspressoIdlingResource.increment(); // App is busy until further notice

   mNotesRepository.getNotes(new NotesRepository.LoadNotesCallback() {
       @Override
       public void onNotesLoaded(List<Note> notes) {
           EspressoIdlingResource.decrement(); // Set app as idle.
           mNotesView.setProgressIndicator(false);
           mNotesView.showNotes(notes);
       }
   });
}

निर्भरता

androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
    implementation 'androidx.test.espresso:espresso-idling-resource:3.1.1'

Androidx के लिए

androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    implementation 'com.android.support.test.espresso:espresso-idling-resource:3.0.2'

आधिकारिक रिपो: https://github.com/googlecodelabs/android-testing

IdlingResource उदाहरण: https://github.com/googlesamples/android-testing/tree/master/ui/espresso/IdlingResourceSample


0

जबकि मुझे लगता है कि इसके लिए आइडलिंग रिसोर्सेस का उपयोग करना सबसे अच्छा है ( https://google.github.io/android-testing-support-library/docs/espresso/idling-resource/ ) शायद आप इसका उपयोग फॉलबैक के रूप में कर सकते हैं:

/**
 * Contains view interactions, view actions and view assertions which allow to set a timeout
 * for finding a view and performing an action/view assertion on it.
 * To be used instead of {@link Espresso}'s methods.
 * 
 * @author Piotr Zawadzki
 */
public class TimeoutEspresso {

    private static final int SLEEP_IN_A_LOOP_TIME = 50;

    private static final long DEFAULT_TIMEOUT_IN_MILLIS = 10 * 1000L;

    /**
     * Use instead of {@link Espresso#onView(Matcher)}
     * @param timeoutInMillis timeout after which an error is thrown
     * @param viewMatcher view matcher to check for view
     * @return view interaction
     */
    public static TimedViewInteraction onViewWithTimeout(long timeoutInMillis, @NonNull final Matcher<View> viewMatcher) {

        final long startTime = System.currentTimeMillis();
        final long endTime = startTime + timeoutInMillis;

        do {
            try {
                return new TimedViewInteraction(Espresso.onView(viewMatcher));
            } catch (NoMatchingViewException ex) {
                //ignore
            }

            SystemClock.sleep(SLEEP_IN_A_LOOP_TIME);
        }
        while (System.currentTimeMillis() < endTime);

        // timeout happens
        throw new PerformException.Builder()
                .withCause(new TimeoutException("Timeout occurred when trying to find: " + viewMatcher.toString()))
                .build();
    }

    /**
     * Use instead of {@link Espresso#onView(Matcher)}.
     * Same as {@link #onViewWithTimeout(long, Matcher)} but with the default timeout {@link #DEFAULT_TIMEOUT_IN_MILLIS}.
     * @param viewMatcher view matcher to check for view
     * @return view interaction
     */
    public static TimedViewInteraction onViewWithTimeout(@NonNull final Matcher<View> viewMatcher) {
        return onViewWithTimeout(DEFAULT_TIMEOUT_IN_MILLIS, viewMatcher);
    }

    /**
     * A wrapper around {@link ViewInteraction} which allows to set timeouts for view actions and assertions.
     */
    public static class TimedViewInteraction {

        private ViewInteraction wrappedViewInteraction;

        public TimedViewInteraction(ViewInteraction wrappedViewInteraction) {
            this.wrappedViewInteraction = wrappedViewInteraction;
        }

        /**
         * @see ViewInteraction#perform(ViewAction...)
         */
        public TimedViewInteraction perform(final ViewAction... viewActions) {
            wrappedViewInteraction.perform(viewActions);
            return this;
        }

        /**
         * {@link ViewInteraction#perform(ViewAction...)} with a timeout of {@link #DEFAULT_TIMEOUT_IN_MILLIS}.
         * @see ViewInteraction#perform(ViewAction...)
         */
        public TimedViewInteraction performWithTimeout(final ViewAction... viewActions) {
            return performWithTimeout(DEFAULT_TIMEOUT_IN_MILLIS, viewActions);
        }

        /**
         * {@link ViewInteraction#perform(ViewAction...)} with a timeout.
         * @see ViewInteraction#perform(ViewAction...)
         */
        public TimedViewInteraction performWithTimeout(long timeoutInMillis, final ViewAction... viewActions) {
            final long startTime = System.currentTimeMillis();
            final long endTime = startTime + timeoutInMillis;

            do {
                try {
                    return perform(viewActions);
                } catch (RuntimeException ex) {
                    //ignore
                }

                SystemClock.sleep(SLEEP_IN_A_LOOP_TIME);
            }
            while (System.currentTimeMillis() < endTime);

            // timeout happens
            throw new PerformException.Builder()
                    .withCause(new TimeoutException("Timeout occurred when trying to perform view actions: " + viewActions))
                    .build();
        }

        /**
         * @see ViewInteraction#withFailureHandler(FailureHandler)
         */
        public TimedViewInteraction withFailureHandler(FailureHandler failureHandler) {
            wrappedViewInteraction.withFailureHandler(failureHandler);
            return this;
        }

        /**
         * @see ViewInteraction#inRoot(Matcher)
         */
        public TimedViewInteraction inRoot(Matcher<Root> rootMatcher) {
            wrappedViewInteraction.inRoot(rootMatcher);
            return this;
        }

        /**
         * @see ViewInteraction#check(ViewAssertion)
         */
        public TimedViewInteraction check(final ViewAssertion viewAssert) {
            wrappedViewInteraction.check(viewAssert);
            return this;
        }

        /**
         * {@link ViewInteraction#check(ViewAssertion)} with a timeout of {@link #DEFAULT_TIMEOUT_IN_MILLIS}.
         * @see ViewInteraction#check(ViewAssertion)
         */
        public TimedViewInteraction checkWithTimeout(final ViewAssertion viewAssert) {
            return checkWithTimeout(DEFAULT_TIMEOUT_IN_MILLIS, viewAssert);
        }

        /**
         * {@link ViewInteraction#check(ViewAssertion)} with a timeout.
         * @see ViewInteraction#check(ViewAssertion)
         */
        public TimedViewInteraction checkWithTimeout(long timeoutInMillis, final ViewAssertion viewAssert) {
            final long startTime = System.currentTimeMillis();
            final long endTime = startTime + timeoutInMillis;

            do {
                try {
                    return check(viewAssert);
                } catch (RuntimeException ex) {
                    //ignore
                }

                SystemClock.sleep(SLEEP_IN_A_LOOP_TIME);
            }
            while (System.currentTimeMillis() < endTime);

            // timeout happens
            throw new PerformException.Builder()
                    .withCause(new TimeoutException("Timeout occurred when trying to check: " + viewAssert.toString()))
                    .build();
        }
    }
}

और फिर अपने कोड में इसे कॉल करें जैसे:

onViewWithTimeout(withId(R.id.button).perform(click());

के बजाय

onView(withId(R.id.button).perform(click());

यह आपको क्रियाओं को देखने और अभिकथन के लिए टाइमआउट जोड़ने की अनुमति देता है।


किसी भी टेस्ट एस्प्रेसो टेस्ट केस के लिए कोड की सिंगल लाइन के नीचे इसका उपयोग करें: SystemClock.sleep (1000); // 1 दूसरा
निकुंजकुमार कपुपारा

मेरे लिए यह केवल इस लाइन return new TimedViewInteraction(Espresso.onView(viewMatcher));को बदलकर काम करता हैreturn new TimedViewInteraction(Espresso.onView(viewMatcher).check(matches(isDisplayed())));
मैनुअल स्मिट्ज़बर्गर

0

मेरी उपयोगिता रननीय या कॉल करने योग्य निष्पादन को दोहराती है जब तक कि यह त्रुटियों के बिना गुजरता है या टाइमआउट के बाद फेंकने योग्य नहीं है। यह एस्प्रेसो परीक्षणों के लिए पूरी तरह से काम करता है!

मान लीजिए कि अंतिम दृश्य इंटरैक्शन (बटन क्लिक) कुछ पृष्ठभूमि थ्रेड्स (नेटवर्क, डेटाबेस आदि) को सक्रिय करता है। परिणामस्वरूप, एक नई स्क्रीन दिखाई देनी चाहिए और हम इसे अपने अगले चरण में जांचना चाहते हैं, लेकिन हमें नहीं पता कि नई स्क्रीन कब परीक्षण के लिए तैयार होगी।

अनुशंसित दृष्टिकोण आपके ऐप को आपके परीक्षण के लिए थ्रेड राज्यों के बारे में संदेश भेजने के लिए बाध्य करने के लिए है। कभी-कभी हम अंतर्निहित तंत्रों जैसे OkHttp3IdlingResource का उपयोग कर सकते हैं। अन्य मामलों में, आपको केवल परीक्षण समर्थन के लिए अपने ऐप स्रोतों के विभिन्न स्थानों में कोड के टुकड़े डालने चाहिए (आपको ऐप लॉजिक जानना चाहिए!)। इसके अलावा, हमें आपके सभी एनिमेशन बंद कर देने चाहिए (हालाँकि यह UI का हिस्सा है)।

अन्य दृष्टिकोण प्रतीक्षा कर रहा है, उदाहरण के लिए SystemClock.sleep (10000)। लेकिन हम नहीं जानते कि कब तक इंतजार किया जाए और लंबी देरी भी सफलता की गारंटी नहीं दे सकती। दूसरी ओर, आपका परीक्षण लंबे समय तक चलेगा।

मेरा दृष्टिकोण बातचीत को देखने के लिए समय की स्थिति जोड़ना है। जैसे हम परीक्षण करते हैं कि नई स्क्रीन 10000 mc (टाइमआउट) के दौरान दिखाई देनी चाहिए। लेकिन हम प्रतीक्षा नहीं करते हैं और इसे जितनी जल्दी चाहते हैं उतनी जल्दी से जांचते हैं (उदाहरण के लिए हर 100 एमएस), हम इस तरह से टेस्ट थ्रेड को रोकते हैं, लेकिन आमतौर पर, ऐसे मामलों में हमें इसकी आवश्यकता होती है।

Usage:

long timeout=10000;
long matchDelay=100; //(check every 100 ms)
EspressoExecutor myExecutor = new EspressoExecutor<ViewInteraction>(timeout, matchDelay);

ViewInteraction loginButton = onView(withId(R.id.login_btn));
loginButton.perform(click());

myExecutor.callForResult(()->onView(allOf(withId(R.id.title),isDisplayed())));

यह मेरा वर्ग स्रोत है:

/**
 * Created by alexshr on 02.05.2017.
 */

package com.skb.goodsapp;

import android.os.SystemClock;
import android.util.Log;

import java.util.Date;
import java.util.concurrent.Callable;

/**
 * The utility repeats runnable or callable executing until it pass without errors or throws throwable after timeout.
 * It works perfectly for Espresso tests.
 * <p>
 * Suppose the last view interaction (button click) activates some background threads (network, database etc.).
 * As the result new screen should appear and we want to check it in our next step,
 * but we don't know when new screen will be ready to be tested.
 * <p>
 * Recommended approach is to force your app to send messages about threads states to your test.
 * Sometimes we can use built-in mechanisms like OkHttp3IdlingResource.
 * In other cases you should insert code pieces in different places of your app sources (you should known app logic!) for testing support only.
 * Moreover, we should turn off all your animations (although it's the part on ui).
 * <p>
 * The other approach is waiting, e.g. SystemClock.sleep(10000). But we don't known how long to wait and even long delays can't guarantee success.
 * On the other hand your test will last long.
 * <p>
 * My approach is to add time condition to view interaction. E.g. we test that new screen should appear during 10000 mc (timeout).
 * But we don't wait and check new screen as quickly as it appears.
 * Of course, we block test thread such way, but usually it's just what we need in such cases.
 * <p>
 * Usage:
 * <p>
 * long timeout=10000;
 * long matchDelay=100; //(check every 100 ms)
 * EspressoExecutor myExecutor = new EspressoExecutor<ViewInteraction>(timeout, matchDelay);
 * <p>
 * ViewInteraction loginButton = onView(withId(R.id.login_btn));
 * loginButton.perform(click());
 * <p>
 * myExecutor.callForResult(()->onView(allOf(withId(R.id.title),isDisplayed())));
 */
public class EspressoExecutor<T> {

    private static String LOG = EspressoExecutor.class.getSimpleName();

    public static long REPEAT_DELAY_DEFAULT = 100;
    public static long BEFORE_DELAY_DEFAULT = 0;

    private long mRepeatDelay;//delay between attempts
    private long mBeforeDelay;//to start attempts after this initial delay only

    private long mTimeout;//timeout for view interaction

    private T mResult;

    /**
     * @param timeout     timeout for view interaction
     * @param repeatDelay - delay between executing attempts
     * @param beforeDelay - to start executing attempts after this delay only
     */

    public EspressoExecutor(long timeout, long repeatDelay, long beforeDelay) {
        mRepeatDelay = repeatDelay;
        mBeforeDelay = beforeDelay;
        mTimeout = timeout;
        Log.d(LOG, "created timeout=" + timeout + " repeatDelay=" + repeatDelay + " beforeDelay=" + beforeDelay);
    }

    public EspressoExecutor(long timeout, long repeatDelay) {
        this(timeout, repeatDelay, BEFORE_DELAY_DEFAULT);
    }

    public EspressoExecutor(long timeout) {
        this(timeout, REPEAT_DELAY_DEFAULT);
    }


    /**
     * call with result
     *
     * @param callable
     * @return callable result
     * or throws RuntimeException (test failure)
     */
    public T call(Callable<T> callable) {
        call(callable, null);
        return mResult;
    }

    /**
     * call without result
     *
     * @param runnable
     * @return void
     * or throws RuntimeException (test failure)
     */
    public void call(Runnable runnable) {
        call(runnable, null);
    }

    private void call(Object obj, Long initialTime) {
        try {
            if (initialTime == null) {
                initialTime = new Date().getTime();
                Log.d(LOG, "sleep delay= " + mBeforeDelay);
                SystemClock.sleep(mBeforeDelay);
            }

            if (obj instanceof Callable) {
                Log.d(LOG, "call callable");
                mResult = ((Callable<T>) obj).call();
            } else {
                Log.d(LOG, "call runnable");
                ((Runnable) obj).run();
            }
        } catch (Throwable e) {
            long remain = new Date().getTime() - initialTime;
            Log.d(LOG, "remain time= " + remain);
            if (remain > mTimeout) {
                throw new RuntimeException(e);
            } else {
                Log.d(LOG, "sleep delay= " + mRepeatDelay);
                SystemClock.sleep(mRepeatDelay);
                call(obj, initialTime);
            }
        }
    }
}

https://gist.github.com/alexshr/ca90212e49e74eb201fbc976255b47e0


0

यह एक सहायक है जिसका उपयोग मैं कोटलिन में एंड्रॉइड टेस्ट के लिए कर रहा हूं। मेरे मामले में मैं सर्वर प्रतिक्रिया की नकल करने के लिए longOperation का उपयोग कर रहा हूं लेकिन आप इसे अपने उद्देश्य के लिए ट्विक कर सकते हैं।

@Test
fun ensureItemDetailIsCalledForRowClicked() {
    onView(withId(R.id.input_text))
        .perform(ViewActions.typeText(""), ViewActions.closeSoftKeyboard())
    onView(withId(R.id.search_icon)).perform(ViewActions.click())
    longOperation(
        longOperation = { Thread.sleep(1000) },
        callback = {onView(withId(R.id.result_list)).check(isVisible())})
}

private fun longOperation(
    longOperation: ()-> Unit,
    callback: ()-> Unit
){
    Thread{
        longOperation()
        callback()
    }.start()
}

0

मैं इस मिश्रण को करने का अपना तरीका जोड़ूंगा:

fun suspendUntilSuccess(actionToSucceed: () -> Unit, iteration : Int = 0) {
    try {
        actionToSucceed.invoke()
    } catch (e: Throwable) {
        Thread.sleep(200)
        val incrementedIteration : Int = iteration + 1
        if (incrementedIteration == 25) {
            fail("Failed after waiting for action to succeed for 5 seconds.")
        }
        suspendUntilSuccess(actionToSucceed, incrementedIteration)
    }
}

इस तरह कहा जाता है:

suspendUntilSuccess({
    checkThat.viewIsVisible(R.id.textView)
})

आप अधिकतम पुनरावृत्तियों, पुनरावृत्ति की लंबाई, आदि जैसे मानदंड जोड़ सकते हैं।

मैं अभी भी बेकार संसाधनों का उपयोग करना पसंद करता हूं, लेकिन जब उदाहरण के लिए डिवाइस पर धीमी एनिमेशन के कारण परीक्षण कार्य कर रहे हैं, तो मैं इस फ़ंक्शन का उपयोग करता हूं और यह अच्छी तरह से काम करता है। यह निश्चित रूप से 5 सेकंड तक लटका रह सकता है क्योंकि यह विफल होने से पहले है, इसलिए यह आपके परीक्षणों के निष्पादन समय को बढ़ा सकता है यदि सफल होने के लिए कार्रवाई कभी सफल नहीं होती है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.