এসপ্রেসো রেসিপি

এই ডকুমেন্ট থেকে আপনি জানতে পারবেন যে কীভাবে বিভিন্ন ধরনের সাধারণ Espresso টেস্ট সেট-আপ করতে হয়।

একটি ভিউয়ের পাশে আরেকটি ভিউ ম্যাচ করতে

একটি লেআউটে নির্দিষ্ট কিছু ভিউ থাকতে পারে যা স্বতন্ত্র নয়। যেমন, পরিচিতির সারণীতে বারবার দেখানো কল বোতামের একই R.id থাকতে পারে, একই টেক্সট থাকতে পারে এবং ভিউ হায়ারার্কিতে অন্যান্য কল বোতামের মতো একই প্রপার্টি থাকতে পারে।

যেমন, এই অ্যাক্টিভিটিতে, "7" টেক্সট সহ ভিউ একাধিক সারি জুড়ে রিপিট হয়:

একটি তালিকা অ্যাক্টিভিটি যা ৩-আইটেমের তালিকার মধ্যে একই ভিউ এলিমেন্টের ৩টি কপি দেখায়

প্রায়ই, অনন্য নয় এমন ভিউকে অনন্য লেবেলের সাথে পেয়ার করা হয় যা এটির পাশে অবস্থিত, যেমন কল বোতামের পাশে পরিচিতির নাম। এই ক্ষেত্রে, আপনার বেছে নেওয়া বিকল্প আরও নির্দিষ্ট করতে hasSibling() ম্যাচিং টুল ব্যবহার করতে পারেন:

Kotlin

onView(allOf(withText("7"), hasSibling(withText("item: 0"))))
    .perform(click())

জাভা

onView(allOf(withText("7"), hasSibling(withText("item: 0"))))
    .perform(click());

অ্যাকশন বারের মধ্যে থাকা কোনও ভিউয়ের সাথে ম্যাচ করুন

ActionBarTestActivity-এ দুটি আলাদা অ্যাকশন বার আছে: একটি সাধারণ অ্যাকশন বার এবং একটি প্রাসঙ্গিক অ্যাকশন বার যা বিকল্পের মেনু থেকে তৈরি করা হয়। দুটি অ্যাকশন বারে একটি আইটেম আছে যা সবসময় দেখা যায় এবং দুটি আইটেম আছে যা শুধুমাত্র ওভারফ্লো মেনুতে দেখা যায়। কোনও আইটেমে ক্লিক করা হলে, এটি TextView-কে ক্লিক করা আইটেমের কন্টেন্টে পরিবর্তন করে।

দুটি অ্যাকশন বারে দৃশ্যমান আইকন মেলানো সহজ, যেমনটি নিম্নলিখিত কোড স্নিপেটে দেখানো হয়েছে:

Kotlin

fun testClickActionBarItem() {
    // We make sure the contextual action bar is hidden.
    onView(withId(R.id.hide_contextual_action_bar))
        .perform(click())

    // Click on the icon - we can find it by the r.Id.
    onView(withId(R.id.action_save))
        .perform(click())

    // Verify that we have really clicked on the icon
    // by checking the TextView content.
    onView(withId(R.id.text_action_bar_result))
        .check(matches(withText("Save")))
}

জাভা

public void testClickActionBarItem() {
    // We make sure the contextual action bar is hidden.
    onView(withId(R.id.hide_contextual_action_bar))
        .perform(click());

    // Click on the icon - we can find it by the r.Id.
    onView(withId(R.id.action_save))
        .perform(click());

    // Verify that we have really clicked on the icon
    // by checking the TextView content.
    onView(withId(R.id.text_action_bar_result))
        .check(matches(withText("Save")));
}

অ্যাক্টিভিটির একদম উপরে, অ্যাকশন বারে সেভ করার বোতাম আছে

প্রাসঙ্গিক অ্যাকশন বারের জন্য কোডটি একই রকম দেখায়:

Kotlin

fun testClickActionModeItem() {
    // Make sure we show the contextual action bar.
    onView(withId(R.id.show_contextual_action_bar))
        .perform(click())

    // Click on the icon.
    onView((withId(R.id.action_lock)))
        .perform(click())

    // Verify that we have really clicked on the icon
    // by checking the TextView content.
    onView(withId(R.id.text_action_bar_result))
        .check(matches(withText("Lock")))
}

জাভা

public void testClickActionModeItem() {
    // Make sure we show the contextual action bar.
    onView(withId(R.id.show_contextual_action_bar))
        .perform(click());

    // Click on the icon.
    onView((withId(R.id.action_lock)))
        .perform(click());

    // Verify that we have really clicked on the icon
    // by checking the TextView content.
    onView(withId(R.id.text_action_bar_result))
        .check(matches(withText("Lock")));
}

অ্যাক্টিভিটির উপরে অ্যাকশন বারে লক বোতামটি আছে

ওভারফ্লো মেনুতে আইটেমে ক্লিক করা সাধারণ অ্যাকশন বারের জন্য একটু কঠিন কারণ কিছু ডিভাইসে হার্ডওয়্যার ওভারফ্লো মেনু বোতাম থাকে, যা বিকল্প মেনুতে ওভারফ্লো আইটেমগুলি খোলে এবং কিছু ডিভাইসে সফ্টওয়্যার ওভারফ্লো মেনু বোতাম থাকে, যা একটি সাধারণ ওভারফ্লো মেনু খোলে। ভাগ্যিস, Espresso আমাদের জন্য সেটা ম্যানেজ করে।

সাধারণ অ্যাকশন বারের জন্য:

Kotlin

fun testActionBarOverflow() {
    // Make sure we hide the contextual action bar.
    onView(withId(R.id.hide_contextual_action_bar))
        .perform(click())

    // Open the options menu OR open the overflow menu, depending on whether
    // the device has a hardware or software overflow menu button.
    openActionBarOverflowOrOptionsMenu(
            ApplicationProvider.getApplicationContext<Context>())

    // Click the item.
    onView(withText("World"))
        .perform(click())

    // Verify that we have really clicked on the icon by checking
    // the TextView content.
    onView(withId(R.id.text_action_bar_result))
        .check(matches(withText("World")))
}

জাভা

public void testActionBarOverflow() {
    // Make sure we hide the contextual action bar.
    onView(withId(R.id.hide_contextual_action_bar))
        .perform(click());

    // Open the options menu OR open the overflow menu, depending on whether
    // the device has a hardware or software overflow menu button.
    openActionBarOverflowOrOptionsMenu(
            ApplicationProvider.getApplicationContext());

    // Click the item.
    onView(withText("World"))
        .perform(click());

    // Verify that we have really clicked on the icon by checking
    // the TextView content.
    onView(withId(R.id.text_action_bar_result))
        .check(matches(withText("World")));
}

ওভারফ্লো মেনু বোতামটি দেখা যাচ্ছে এবং স্ক্রিনের উপরের দিকে অ্যাকশন বারের নিচে
          একটি তালিকা দেখা যাচ্ছে

হার্ডওয়্যার ওভারফ্লো মেনু বোতাম সহ ডিভাইসে এটি এইভাবে দেখা যায়:

কোনও ওভারফ্লো মেনু বোতাম নেই এবং স্ক্রিনের নিচের দিকে একটি তালিকা
          দেখা যায়

প্রাসঙ্গিক অ্যাকশন বারের জন্য এটি আবার সত্যিই সহজ:

Kotlin

fun testActionModeOverflow() {
    // Show the contextual action bar.
    onView(withId(R.id.show_contextual_action_bar))
        .perform(click())

    // Open the overflow menu from contextual action mode.
    openContextualActionModeOverflowMenu()

    // Click on the item.
    onView(withText("Key"))
        .perform(click())

    // Verify that we have really clicked on the icon by
    // checking the TextView content.
    onView(withId(R.id.text_action_bar_result))
        .check(matches(withText("Key")))
    }
}

জাভা

public void testActionModeOverflow() {
    // Show the contextual action bar.
    onView(withId(R.id.show_contextual_action_bar))
        .perform(click());

    // Open the overflow menu from contextual action mode.
    openContextualActionModeOverflowMenu();

    // Click on the item.
    onView(withText("Key"))
        .perform(click());

    // Verify that we have really clicked on the icon by
    // checking the TextView content.
    onView(withId(R.id.text_action_bar_result))
        .check(matches(withText("Key")));
    }
}

ওভারফ্লো মেনু বোতামটি অ্যাকশন বারে দেখা যায় এবং বিকল্পের তালিকাটি
          অ্যাকশন বারের নিচে, স্ক্রিনের উপরের দিকে দেখা যায়

এইসব স্যাম্পেলের সম্পূর্ণ কোড দেখতে, GitHub-এ ActionBarTest.java স্যাম্পেল দেখুন।

কোনও ভিউ দেখানো হচ্ছে না বলে দাবি করুন

একাধিক অ্যাকশন করার পরে, আপনি অবশ্যই টেস্টের অধীনে UI-এর অবস্থা নিশ্চিত করতে চাইবেন। কখনও কখনও, এটি একটি নেতিবাচক ঘটনা হতে পারে, যেমন যখন কিছু ঘটছে না। মনে রাখবেন যে আপনি ViewAssertions.matches() ব্যবহার করে যেকোনও hamcrest ভিউ ম্যাচারকে ViewAssertion-এ পরিবর্তন করতে পারবেন।

নিচের উদাহরণে, আমরা isDisplayed() ম্যাচিং টুল নিয়েছি এবং স্ট্যান্ডার্ড not() ম্যাচিং টুল ব্যবহার করে এটি রিভার্স করেছি:

Kotlin

import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import org.hamcrest.Matchers.not

onView(withId(R.id.bottom_left))
    .check(matches(not(isDisplayed())))

জাভা

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static org.hamcrest.Matchers.not;

onView(withId(R.id.bottom_left))
    .check(matches(not(isDisplayed())));

ভিউ এখনও হায়ারার্কির অংশ হলে উপরের পদ্ধতি কাজ করে। যদি তা না হয়, তাহলে আপনি NoMatchingViewException পাবেন এবং আপনাকে ViewAssertions.doesNotExist() ব্যবহার করতে হবে।

ভিউ নেই বলে দাবি করুন

ভিউ হায়ারার্কি থেকে ভিউ চলে গেলে—যা কোনও অ্যাকশনের কারণে অন্য অ্যাক্টিভিটিতে ট্রানজিশন হলে ঘটতে পারে—আপনাকে ViewAssertions.doesNotExist() ব্যবহার করতে হবে:

Kotlin

import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
import androidx.test.espresso.matcher.ViewMatchers.withId

onView(withId(R.id.bottom_left))
    .check(doesNotExist())

জাভা

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.matcher.ViewMatchers.withId;

onView(withId(R.id.bottom_left))
    .check(doesNotExist());

ডেটা আইটেম অ্যাডাপ্টারে নেই বলে দাবি করুন

কোনও নির্দিষ্ট ডেটা আইটেম যে AdapterView-এর মধ্যে নেই তা প্রমাণ করতে আপনাকে একটু অন্যভাবে কাজ করতে হবে। আমাদের আগ্রহ আছে এমন AdapterView খুঁজে বের করতে হবে এবং এতে থাকা ডেটা পরীক্ষা করতে হবে। আমাদের onData() ব্যবহার করার প্রয়োজন নেই। পরিবর্তে, আমরা AdapterView খুঁজতে onView() ব্যবহার করি এবং তারপরে ভিউয়ের মধ্যে ডেটা নিয়ে কাজ করার জন্য অন্য একটি ম্যাচার ব্যবহার করি।

প্রথমে ম্যাচিং টুল:

Kotlin

private fun withAdaptedData(dataMatcher: Matcher<Any>): Matcher<View> {
    return object : TypeSafeMatcher<View>() {

        override fun describeTo(description: Description) {
            description.appendText("with class name: ")
            dataMatcher.describeTo(description)
        }

        public override fun matchesSafely(view: View) : Boolean {
            if (view !is AdapterView<*>) {
                return false
            }

            val adapter = view.adapter
            for (i in 0 until adapter.count) {
                if (dataMatcher.matches(adapter.getItem(i))) {
                    return true
                }
            }

            return false
        }
    }
}

জাভা

private static Matcher<View> withAdaptedData(final Matcher<Object> dataMatcher) {
    return new TypeSafeMatcher<View>() {

        @Override
        public void describeTo(Description description) {
            description.appendText("with class name: ");
            dataMatcher.describeTo(description);
        }

        @Override
        public boolean matchesSafely(View view) {
            if (!(view instanceof AdapterView)) {
                return false;
            }

            @SuppressWarnings("rawtypes")
            Adapter adapter = ((AdapterView) view).getAdapter();
            for (int i = 0; i < adapter.getCount(); i++) {
                if (dataMatcher.matches(adapter.getItem(i))) {
                    return true;
                }
            }

            return false;
        }
    };
}

তাহলে AdapterView খুঁজে পেতে আমাদের শুধু onView() দরকার:

Kotlin

fun testDataItemNotInAdapter() {
    onView(withId(R.id.list))
          .check(matches(not(withAdaptedData(withItemContent("item: 168")))))
    }
}

জাভা

@SuppressWarnings("unchecked")
public void testDataItemNotInAdapter() {
    onView(withId(R.id.list))
          .check(matches(not(withAdaptedData(withItemContent("item: 168")))));
    }
}

এবং আমাদের কাছে একটি অ্যাসারশন আছে যা ব্যর্থ হবে যদি "item: 168" এর সমান একটি আইটেম আইডি তালিকা সহ একটি অ্যাডাপ্টার ভিউতে বিদ্যমান থাকে।

সম্পূর্ণ স্যাম্পেল দেখতে, GitHub-এ AdapterViewTest.java ক্লাসের মধ্যে testDataItemNotInAdapter() পদ্ধতি দেখুন।

কাস্টম ফেলিয়র হ্যান্ডলার ব্যবহার করুন

Espresso-তে ডিফল্ট FailureHandler-এর পরিবর্তে কাস্টম ব্যবহার করলে অতিরিক্ত বা অন্য ধরনের সমস্যা সমাধান করা যায়, যেমন স্ক্রিনশট নেওয়া বা অতিরিক্ত ডিবাগ তথ্য পাঠানো।

CustomFailureHandlerTest উদাহরণে দেখানো হয়েছে কীভাবে কাস্টম ব্যর্থতা হ্যান্ডলার প্রয়োগ করতে হয়:

Kotlin

private class CustomFailureHandler(targetContext: Context) : FailureHandler {
    private val delegate: FailureHandler

    init {
        delegate = DefaultFailureHandler(targetContext)
    }

    override fun handle(error: Throwable, viewMatcher: Matcher<View>) {
        try {
            delegate.handle(error, viewMatcher)
        } catch (e: NoMatchingViewException) {
            throw MySpecialException(e)
        }

    }
}

জাভা

private static class CustomFailureHandler implements FailureHandler {
    private final FailureHandler delegate;

    public CustomFailureHandler(Context targetContext) {
        delegate = new DefaultFailureHandler(targetContext);
    }

    @Override
    public void handle(Throwable error, Matcher<View> viewMatcher) {
        try {
            delegate.handle(error, viewMatcher);
        } catch (NoMatchingViewException e) {
            throw new MySpecialException(e);
        }
    }
}

এই ফেলিওর হ্যান্ডলার NoMatchingViewException-এর পরিবর্তে MySpecialException থ্রো করে এবং অন্য সব ফেলিওর DefaultFailureHandler-এ ডেলিগেট করে। CustomFailureHandler-কে টেস্টের setUp() পদ্ধতিতে Espresso-এর সাথে রেজিস্টার করা যেতে পারে:

Kotlin

@Throws(Exception::class)
override fun setUp() {
    super.setUp()
    getActivity()
    setFailureHandler(CustomFailureHandler(
            ApplicationProvider.getApplicationContext<Context>()))
}

জাভা

@Override
public void setUp() throws Exception {
    super.setUp();
    getActivity();
    setFailureHandler(new CustomFailureHandler(
            ApplicationProvider.getApplicationContext()));
}

আরও তথ্যের জন্য, FailureHandler ইন্টারফেস এবং Espresso.setFailureHandler() দেখুন।

ডিফল্ট নয় এমন উইন্ডো টার্গেট করা

Android-এ একাধিক উইন্ডো ব্যবহার করা যায়। সাধারণত, এটি ব্যবহারকারী এবং অ্যাপ ডেভেলপারের কাছে স্বচ্ছ থাকে, তবে কিছু ক্ষেত্রে একাধিক উইন্ডো দেখা যায়, যেমন সার্চ উইজেটে মূল অ্যাপ্লিকেশন উইন্ডোর উপরে যখন একটি অটো-কমপ্লিট উইন্ডো তৈরি হয়। জিনিসগুলি সহজ করার জন্য, ডিফল্টভাবে Espresso একটি হিউরিস্টিক ব্যবহার করে আপনি কোন Window-এর সাথে ইন্টারঅ্যাক্ট করতে চান তা অনুমান করে। এই হিউরিস্টিক প্রায় সবসময়ই যথেষ্ট ভালো; তবে, বিরল ক্ষেত্রে, আপনাকে নির্দিষ্ট করতে হবে যে কোন উইন্ডোকে একটি ইন্টার‍্যাকশন টার্গেট করবে। আপনি নিজের রুট উইন্ডো ম্যাচার বা Root ম্যাচার প্রদান করে এটি করতে পারেন:

Kotlin

onView(withText("South China Sea"))
    .inRoot(withDecorView(not(`is`(getActivity().getWindow().getDecorView()))))
    .perform(click())

জাভা

onView(withText("South China Sea"))
    .inRoot(withDecorView(not(is(getActivity().getWindow().getDecorView()))))
    .perform(click());

ViewMatchers-এর ক্ষেত্রে যেমন হয়, আমরা আগে থেকে প্রদান করা RootMatchers-এর একটি সেট প্রদান করি। অবশ্যই, আপনি যেকোনও সময় নিজের Matcher অবজেক্ট প্রয়োগ করতে পারবেন।

GitHub-এ MultipleWindowTest স্যাম্পেল দেখুন।

addHeaderView() এবং addFooterView() পদ্ধতি ব্যবহার করে ListViews-এ হেডার ও ফুটার যোগ করা হয়। Espresso.onData() যাতে বুঝতে পারে কোন ডেটা অবজেক্ট ম্যাচ করতে হবে, তার জন্য addHeaderView() ও addFooterView()-এ দ্বিতীয় প্যারামিটার হিসেবে প্রিসেট ডেটা অবজেক্ট ভ্যালু পাস করতে ভুলবেন না। যেমন:

Kotlin

const val FOOTER = "FOOTER"
...
val footerView = layoutInflater.inflate(R.layout.list_item, listView, false)
footerView.findViewById<TextView>(R.id.item_content).text = "count:"
footerView.findViewById<TextView>(R.id.item_size).text
        = data.size.toString
listView.addFooterView(footerView, FOOTER, true)

জাভা

public static final String FOOTER = "FOOTER";
...
View footerView = layoutInflater.inflate(R.layout.list_item, listView, false);
footerView.findViewById<TextView>(R.id.item_content).setText("count:");
footerView.findViewById<TextView>(R.id.item_size).setText(String.valueOf(data.size()));
listView.addFooterView(footerView, FOOTER, true);

তারপর, আপনি ফুটারের জন্য একটি ম্যাচিং এক্সপ্রেশন লিখতে পারেন:

Kotlin

import org.hamcrest.Matchers.allOf
import org.hamcrest.Matchers.instanceOf
import org.hamcrest.Matchers.`is`

fun isFooter(): Matcher<Any> {
    return allOf(`is`(instanceOf(String::class.java)),
            `is`(LongListActivity.FOOTER))
}

জাভা

import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;

@SuppressWarnings("unchecked")
public static Matcher<Object> isFooter() {
    return allOf(is(instanceOf(String.class)), is(LongListActivity.FOOTER));
}

And loading the view in a test is trivial:

Kotlin

import androidx.test.espresso.Espresso.onData
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.sample.LongListMatchers.isFooter

fun testClickFooter() {
    onData(isFooter())
        .perform(click())

    // ...
}

জাভা

import static androidx.test.espresso.Espresso.onData;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.sample.LongListMatchers.isFooter;

public void testClickFooter() {
    onData(isFooter())
        .perform(click());

    // ...
}

GitHub-এ AdapterViewTest.java-এর testClickFooter() পদ্ধতিতে সম্পূর্ণ কোডের স্যাম্পেল দেখুন।