স্ব-ইনস্ট্রুমেন্টিং পরীক্ষার উদাহরণ

যখন একটি ইন্সট্রুমেন্টেশন টেস্ট শুরু করা হয়, তখন এর টার্গেট প্যাকেজটি পুনরায় চালু করা হয় এবং ইন্সট্রুমেন্টেশন কোড ইনজেক্ট করে এক্সিকিউশনের জন্য ইনিশিয়েট করা হয়। এর একটি ব্যতিক্রম হলো, এক্ষেত্রে টার্গেট প্যাকেজটি স্বয়ং অ্যান্ড্রয়েড অ্যাপ্লিকেশন ফ্রেমওয়ার্ক (যেমন android প্যাকেজ) হতে পারে না, কারণ তা করলে একটি স্ববিরোধী পরিস্থিতির সৃষ্টি হয়, যেখানে অ্যান্ড্রয়েড ফ্রেমওয়ার্কটিকেই পুনরায় চালু করার প্রয়োজন পড়ে, অথচ এই ফ্রেমওয়ার্কটিই ইন্সট্রুমেন্টেশনসহ সিস্টেমের বিভিন্ন ফাংশনকে সাপোর্ট করে।

এর মানে হলো, একটি ইন্সট্রুমেন্টেশন টেস্ট এক্সিকিউশনের জন্য নিজেকে অ্যান্ড্রয়েড ফ্রেমওয়ার্ক, অর্থাৎ সিস্টেম সার্ভারে, ইনজেক্ট করতে পারে না। অ্যান্ড্রয়েড ফ্রেমওয়ার্ক টেস্ট করার জন্য, টেস্ট কোডটি শুধুমাত্র পাবলিক এপিআই সারফেস, অথবা প্ল্যাটফর্ম সোর্স ট্রিতে উপলব্ধ অ্যান্ড্রয়েড ইন্টারফেস ডেফিনিশন ল্যাঙ্গুয়েজ (AIDL) ব্যবহার করে এক্সপোজ করা এপিআইগুলোকেই কল করতে পারে। এই ধরনের টেস্টের জন্য কোনো নির্দিষ্ট প্যাকেজকে টার্গেট করা অর্থবহ নয়। তাই, এই ধরনের ইন্সট্রুমেন্টেশনগুলোকে তাদের নিজস্ব টেস্ট অ্যাপ্লিকেশন প্যাকেজকে টার্গেট করার জন্য ডিক্লেয়ার করাটাই প্রচলিত, যা AndroidManifest.xml ফাইলের নিজস্ব <manifest> ট্যাগে সংজ্ঞায়িত করা থাকে।

প্রয়োজনীয়তা অনুসারে, এই বিভাগের টেস্ট অ্যাপ্লিকেশন প্যাকেজগুলি আরও যা করতে পারে:

  • পরীক্ষার জন্য প্রয়োজনীয় কার্যক্রমগুলো একত্রিত করুন।
  • সিস্টেমের সাথে ইউজার আইডিটি শেয়ার করুন।
  • প্ল্যাটফর্ম কী দিয়ে সাইন ইন করুন।
  • পাবলিক SDK-এর পরিবর্তে ফ্রেমওয়ার্ক সোর্সের সাথে কম্পাইল করা হোক।

এই শ্রেণীর ইনস্ট্রুমেন্টেশন পরীক্ষাকে কখনও কখনও সেলফ-ইনস্ট্রুমেন্টেশন বলা হয়। প্ল্যাটফর্ম সোর্সে সেলফ-ইনস্ট্রুমেন্টেশন পরীক্ষার কিছু উদাহরণ নিচে দেওয়া হলো:

এখানে যে উদাহরণটি আলোচনা করা হয়েছে, তা হলো নিজস্ব টেস্ট অ্যাপ্লিকেশন প্যাকেজে টার্গেট প্যাকেজ সেট করে একটি নতুন ইন্সট্রুমেন্টেশন টেস্ট লেখা। এই নির্দেশিকাটি উদাহরণ হিসেবে নিম্নলিখিত টেস্টটি ব্যবহার করে:

এগিয়ে যাওয়ার আগে একটি মোটামুটি ধারণা পেতে প্রথমে কোডটি দেখে নেওয়ার পরামর্শ দেওয়া হচ্ছে।

উৎস স্থান নির্ধারণ করুন

সাধারণত আপনার টিমের কোড চেক-ইন করার এবং টেস্ট যোগ করার জন্য একটি প্রতিষ্ঠিত পদ্ধতি আগে থেকেই থাকে। বেশিরভাগ টিমের নিজস্ব একটি গিট রিপোজিটরি থাকে, অথবা তারা অন্য টিমের সাথে একটি শেয়ার করে, কিন্তু কম্পোনেন্টের সোর্স কোড রাখার জন্য একটি ডেডিকেটেড সাব-ডিরেক্টরি থাকে।

ধরে নিচ্ছি আপনার কম্পোনেন্ট সোর্সের মূল অবস্থান হলো <component source root> বেশিরভাগ কম্পোনেন্টের অধীনে src এবং tests ফোল্ডার থাকে এবং এর সাথে কিছু অতিরিক্ত ফাইলও থাকে, যেমন Android.mk (অথবা এটিকে কয়েকটি অতিরিক্ত .mk ফাইলে ভাগ করে), ম্যানিফেস্ট ফাইল AndroidManifest.xml এবং টেস্ট কনফিগারেশন ফাইল 'AndroidTest.xml'।

যেহেতু আপনি একটি একদম নতুন টেস্ট যোগ করছেন, তাই সম্ভবত আপনাকে আপনার কম্পোনেন্ট src পাশে tests ডিরেক্টরিটি তৈরি করতে হবে এবং সেটিকে কন্টেন্ট দিয়ে পূর্ণ করতে হবে।

কিছু ক্ষেত্রে, বিভিন্ন টেস্ট স্যুটকে আলাদা এপিকে-তে প্যাকেজ করার প্রয়োজনের কারণে আপনার টিমের tests অধীনে আরও ডিরেক্টরি কাঠামো থাকতে পারে। এবং এই ক্ষেত্রে, আপনাকে tests অধীনে একটি নতুন সাব-ডিরেক্টরি তৈরি করতে হবে।

কাঠামো যেমনই হোক না কেন, আপনাকে tests ডিরেক্টরি অথবা নতুন তৈরি করা সাব-ডিরেক্টরিটি এমন সব ফাইল দিয়ে পূরণ করতে হবে, যা স্যাম্পল গ্যারিট চেঞ্জের instrumentation ডিরেক্টরিতে থাকা ফাইলগুলোর অনুরূপ। প্রতিটি ফাইলের বিস্তারিত বিবরণ এই ডকুমেন্টের পরবর্তী অংশে ব্যাখ্যা করা হয়েছে।

ম্যানিফেস্ট ফাইল

একটি অ্যাপ প্রজেক্টের মতোই, প্রতিটি ইন্সট্রুমেন্টেশন টেস্ট মডিউলের জন্য AndroidManifest.xml নামের একটি ম্যানিফেস্ট ফাইল প্রয়োজন হয়। BUILD_PACKAGE কোর মেকফাইল ব্যবহার করে এই ফাইলটি স্বয়ংক্রিয়ভাবে অন্তর্ভুক্ত করতে, আপনার টেস্ট মডিউলের Android.mk ফাইলের পাশে এই ফাইলটি সরবরাহ করুন।

আপনি যদি AndroidManifest.xml ফাইলটির সাথে পরিচিত না হন, তাহলে অ্যাপ ম্যানিফেস্ট ওভারভিউ দেখুন।

নিচে একটি নমুনা AndroidManifest.xml ফাইল দেওয়া হলো:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  android:sharedUserId="android.uid.system"
  package="android.test.example.helloworld" >

    <application>
       <uses-library android:name="android.test.runner"/>
    </application>

    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                     android:targetPackage="android.test.example.helloworld"
                     android:label="Hello World Test"/>

</manifest>

ম্যানিফেস্ট ফাইল সম্পর্কে কিছু নির্বাচিত মন্তব্য:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="android.test.example.helloworld" >

package অ্যাট্রিবিউটটি হলো অ্যাপ্লিকেশন প্যাকেজের নাম: এটি একটি অনন্য শনাক্তকারী যা অ্যান্ড্রয়েড অ্যাপ্লিকেশন ফ্রেমওয়ার্ক একটি অ্যাপ্লিকেশনকে (অথবা এই প্রসঙ্গে: আপনার টেস্ট অ্যাপ্লিকেশনকে) শনাক্ত করতে ব্যবহার করে। সিস্টেমের প্রতিটি ব্যবহারকারী সেই প্যাকেজের নামে শুধুমাত্র একটি অ্যাপ্লিকেশন ইনস্টল করতে পারে।

তাছাড়া, এই package অ্যাট্রিবিউটটি ComponentName#getPackageName() যা রিটার্ন করে তার অনুরূপ, এবং adb shell ব্যবহার করে বিভিন্ন pm সাব-কমান্ডের সাথে ইন্টারঅ্যাক্ট করার জন্যও আপনি এটিই ব্যবহার করবেন।

মনে রাখবেন যে, যদিও প্যাকেজের নামটি সাধারণত জাভা প্যাকেজের নামের মতোই হয়, বাস্তবে এর সাথে এর খুব কমই মিল রয়েছে। অন্য কথায়, আপনার অ্যাপ্লিকেশন (বা টেস্ট) প্যাকেজে যেকোনো প্যাকেজের নামের ক্লাস থাকতে পারে, যদিও অন্যদিকে, আপনি সরলতার জন্য আপনার অ্যাপ্লিকেশন বা টেস্টের শীর্ষ-স্তরের জাভা প্যাকেজের নামটি অ্যাপ্লিকেশন প্যাকেজের নামের মতোই রাখতে পারেন।

android:sharedUserId="android.uid.system"

এর মাধ্যমে ঘোষণা করা হয় যে, ইনস্টলেশনের সময় এই APK ফাইলটিকে কোর প্ল্যাটফর্মের মতোই একই ইউজার আইডি, অর্থাৎ রানটাইম আইডেন্টিটি, প্রদান করা হবে। উল্লেখ্য যে, এটি নির্ভর করে APK ফাইলটি কোর প্ল্যাটফর্মের মতো একই সার্টিফিকেট দ্বারা স্বাক্ষরিত হওয়ার উপর (পূর্ববর্তী একটি বিভাগে LOCAL_CERTIFICATE দেখুন), তবে এগুলি ভিন্ন ধারণা:

  • কিছু পারমিশন বা এপিআই সিগনেচার দ্বারা সুরক্ষিত থাকে, যার জন্য একই সাইনিং সার্টিফিকেট প্রয়োজন হয়।
  • কিছু পারমিশন বা এপিআই-এর জন্য কলারের system ইউজার আইডেন্টিটি প্রয়োজন হয়, যার জন্য কলিং প্যাকেজটিকে system সাথে ইউজার আইডি শেয়ার করতে হয়, যদি এটি কোর প্ল্যাটফর্ম থেকে একটি আলাদা প্যাকেজ হয়।
<uses-library android:name="android.test.runner" />

সমস্ত ইন্সট্রুমেন্টেশন টেস্টের জন্য এটি প্রয়োজন, কারণ সংশ্লিষ্ট ক্লাসগুলি একটি পৃথক ফ্রেমওয়ার্ক JAR লাইব্রেরি ফাইলে প্যাকেজ করা থাকে, ফলে অ্যাপ্লিকেশন ফ্রেমওয়ার্ক দ্বারা টেস্ট প্যাকেজটি আহ্বান করা হলে অতিরিক্ত ক্লাসপাথ এন্ট্রির প্রয়োজন হয়।

android:targetPackage="android.test.example.helloworld"

আপনি হয়তো লক্ষ্য করেছেন যে, এখানে targetPackage এই ফাইলের manifest ট্যাগে ঘোষিত package অ্যাট্রিবিউটের মতোই ঘোষণা করা হয়েছে। টেস্টিং বেসিকস- এ যেমন উল্লেখ করা হয়েছে, এই ধরনের ইন্সট্রুমেন্টেশন টেস্ট সাধারণত ফ্রেমওয়ার্ক এপিআই (API) পরীক্ষা করার জন্য করা হয়, তাই এটি ছাড়া অন্য কোনো নির্দিষ্ট অ্যাপ্লিকেশন প্যাকেজকে লক্ষ্য করা খুব একটা অর্থবহ নয়।

সাধারণ কনফিগারেশন ফাইল

প্রতিটি নতুন টেস্ট মডিউলের একটি কনফিগারেশন ফাইল থাকা আবশ্যক, যা মডিউল মেটাডেটা, কম্পাইল-টাইম ডিপেন্ডেন্সি এবং প্যাকেজিং নির্দেশাবলী দিয়ে বিল্ড সিস্টেমকে পরিচালনা করে। বেশিরভাগ ক্ষেত্রে, সুং-ভিত্তিক ব্লুপ্রিন্ট ফাইল বিকল্পটিই যথেষ্ট। বিস্তারিত জানতে, সিম্পল টেস্ট কনফিগারেশন দেখুন।

জটিল কনফিগারেশন ফাইল

এই আরও জটিল ক্ষেত্রগুলোর জন্য, আপনাকে অ্যান্ড্রয়েডের টেস্ট হারনেস, ট্রেড ফেডারেশন- এর জন্য একটি টেস্ট কনফিগারেশন ফাইলও লিখতে হবে।

টেস্ট কনফিগারেশনে টেস্ট ক্লাসে সরবরাহ করার জন্য বিশেষ ডিভাইস সেটআপ অপশন এবং ডিফল্ট আর্গুমেন্ট নির্দিষ্ট করা যেতে পারে। /platform_testing/tests/example/instrumentation/AndroidTest.xml -এ উদাহরণটি দেখুন।

সুবিধার জন্য এখানে একটি স্ন্যাপশট অন্তর্ভুক্ত করা হয়েছে:

<configuration description="Runs sample instrumentation test.">
  <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
  <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
    <option name="test-file-name" value="HelloWorldTests.apk"/>
  </target_preparer>
  <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
  <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
  <option name="test-suite-tag" value="apct"/>
  <option name="test-tag" value="SampleInstrumentationTest"/>

  <test class="com.android.tradefed.testtype.AndroidJUnitTest">
    <option name="package" value="android.test.example.helloworld"/>
    <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
  </test>
</configuration>

টেস্ট কনফিগারেশন ফাইল সম্পর্কে কিছু নির্বাচিত মন্তব্য:

<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
  <option name="test-file-name" value="HelloWorldTests.apk"/>
</target_preparer>

এটি ট্রেড ফেডারেশনকে একটি নির্দিষ্ট target_preparer ব্যবহার করে টার্গেট ডিভাইসে HelloWorldTests.apk ইনস্টল করতে নির্দেশ দেয়। ট্রেড ফেডারেশনে ডেভেলপারদের জন্য অনেক target preparer উপলব্ধ রয়েছে এবং টেস্ট সম্পাদনের আগে ডিভাইসটি সঠিকভাবে সেটআপ করা হয়েছে কিনা তা নিশ্চিত করতে এগুলি ব্যবহার করা যেতে পারে।

<test class="com.android.tradefed.testtype.AndroidJUnitTest">
  <option name="package" value="android.test.example.helloworld"/>
  <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
</test>

এটি পরীক্ষাটি চালানোর জন্য ব্যবহৃত ট্রেড ফেডারেশন টেস্ট ক্লাসকে নির্দিষ্ট করে এবং যে ডিভাইসে পরীক্ষাটি চালানো হবে তার প্যাকেজ ও টেস্ট রানার ফ্রেমওয়ার্ক, যা এই ক্ষেত্রে JUnit, তা পাস করে দেয়।

আরও তথ্যের জন্য, টেস্ট মডিউল কনফিগস দেখুন।

JUnit4 বৈশিষ্ট্য

টেস্ট রানার হিসেবে android-support-test লাইব্রেরি ব্যবহার করলে নতুন JUnit4 স্টাইলের টেস্ট ক্লাসগুলো গ্রহণ করা সম্ভব হয়, এবং নমুনা গ্যারিট পরিবর্তনে এর বৈশিষ্ট্যগুলোর কিছু অতি সাধারণ ব্যবহার রয়েছে। উদাহরণটি /platform_testing/tests/example/instrumentation/src/android/test/example/helloworld/HelloWorldTest.java -এ দেখুন।

যদিও টেস্টিং প্যাটার্নগুলো সাধারণত কম্পোনেন্ট টিমের জন্য নির্দিষ্ট হয়ে থাকে, তবুও কিছু সাধারণভাবে কার্যকর ব্যবহারের ধরণ রয়েছে।

@RunWith(JUnit4.class)
public class HelloWorldTest {

JUnit4-এর একটি উল্লেখযোগ্য পার্থক্য হলো, টেস্টগুলোকে আর কোনো সাধারণ বেস টেস্ট ক্লাস থেকে ইনহেরিট করার প্রয়োজন হয় না; এর পরিবর্তে, আপনি সাধারণ জাভা ক্লাসে টেস্ট লেখেন এবং নির্দিষ্ট টেস্ট সেটআপ ও সীমাবদ্ধতা নির্দেশ করতে অ্যানোটেশন ব্যবহার করেন। এই উদাহরণে, আমরা নির্দেশ দিচ্ছি যে এই ক্লাসটি একটি JUnit4 টেস্ট হিসেবে রান করা উচিত।

    @BeforeClass
    public static void beforeClass() {
    ...
    @AfterClass
    public static void afterClass() {
    ...
    @Before
    public void before() {
    ...
    @After
    public void after() {
    ...
    @Test
    @SmallTest
    public void testHelloWorld() {
    ...

JUnit4-এ প্রি-টেস্ট সেটআপ এবং পোস্ট-টেস্ট টিয়ারডাউন করার জন্য মেথডগুলিতে @Before এবং @After অ্যানোটেশন ব্যবহার করা হয়। একইভাবে, একটি টেস্ট ক্লাসের সমস্ত টেস্ট চালানোর আগে সেটআপ এবং পরে টিয়ারডাউন করার জন্য JUnit4-এ মেথডগুলিতে @BeforeClass এবং @AfterClass অ্যানোটেশন ব্যবহার করা হয়। মনে রাখবেন যে ক্লাস-স্কোপের সেটআপ এবং টিয়ারডাউন মেথডগুলি অবশ্যই স্ট্যাটিক হতে হবে। টেস্ট মেথডের ক্ষেত্রে, JUnit-এর আগের সংস্করণগুলির মতো এখন আর মেথডের নাম ' test দিয়ে শুরু করার প্রয়োজন নেই, পরিবর্তে, সেগুলির প্রত্যেকটিকে অবশ্যই @Test দিয়ে অ্যানোটেট করতে হবে। যথারীতি, টেস্ট মেথড অবশ্যই পাবলিক হতে হবে, কোনো রিটার্ন ভ্যালু ডিক্লেয়ার করবে না, কোনো প্যারামিটার নেবে না এবং এক্সেপশন থ্রো করতে পারবে।

ইনস্ট্রুমেন্টেশন ক্লাস অ্যাক্সেস

যদিও সাধারণ হ্যালো ওয়ার্ল্ড উদাহরণে এটি অন্তর্ভুক্ত নয়, অ্যান্ড্রয়েড টেস্টের জন্য Instrumentation ইনস্ট্যান্সে অ্যাক্সেসের প্রয়োজন হওয়াটা বেশ সাধারণ একটি বিষয়: এটি হলো মূল এপিআই ইন্টারফেস যা অ্যাপ্লিকেশন কনটেক্সট, অ্যাক্টিভিটি লাইফসাইকেল সম্পর্কিত টেস্ট এপিআই এবং আরও অনেক কিছুতে অ্যাক্সেস প্রদান করে।

যেহেতু JUnit4 টেস্টগুলোর জন্য এখন আর কোনো সাধারণ বেস ক্লাসের প্রয়োজন হয় না, তাই InstrumentationTestCase#getInstrumentation() এর মাধ্যমে Instrumentation ইনস্ট্যান্স সংগ্রহ করারও আর প্রয়োজন নেই। এর পরিবর্তে, নতুন টেস্ট রানার এটি InstrumentationRegistry মাধ্যমে পরিচালনা করে, যেখানে ইন্সট্রুমেন্টেশন ফ্রেমওয়ার্ক দ্বারা তৈরি প্রাসঙ্গিক এবং পরিবেশগত সেটআপ সংরক্ষিত থাকে।

Instrumentation ক্লাসের ইনস্ট্যান্স অ্যাক্সেস করতে, InstrumentationRegistry ক্লাসের স্ট্যাটিক মেথড getInstrumentation() কল করুন:

Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation()

স্থানীয়ভাবে তৈরি ও পরীক্ষা করুন

সবচেয়ে সাধারণ ব্যবহারের ক্ষেত্রে Atest ব্যবহার করুন।

আরও জটিল ক্ষেত্রগুলির জন্য, যেখানে ব্যাপকতর কাস্টমাইজেশনের প্রয়োজন হয়, সেখানে ইন্সট্রুমেন্টেশন নির্দেশাবলী অনুসরণ করুন।