অ্যান্ড্রয়েড টেস্ট ফ্রেমওয়ার্ক সহ অ্যান্ড্রয়েড অ্যাসিঙ্কটাস্ক পরীক্ষা testing


97

আমার কাছে খুব সহজ অ্যাসিঙ্কটাস্ক বাস্তবায়নের উদাহরণ রয়েছে এবং অ্যান্ড্রয়েড জুনিট ফ্রেমওয়ার্ক ব্যবহার করে এটি পরীক্ষা করতে সমস্যা হচ্ছে।

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

  • এটি doInBackground()পদ্ধতিটি সঠিকভাবে কার্যকর করে
  • তবে এটি তার প্রজ্ঞাপন পদ্ধতি (কোন কিছুকে ডাকে না onPostExecute(), onProgressUpdate()ইত্যাদি) - শুধু চুপটি তাদের কোনো ত্রুটি দেখিয়ে উপেক্ষা করে।

এটি খুব সহজ AsyncTask উদাহরণ:

package kroz.andcookbook.threads.asynctask;

import android.os.AsyncTask;
import android.util.Log;
import android.widget.ProgressBar;
import android.widget.Toast;

public class AsyncTaskDemo extends AsyncTask<Integer, Integer, String> {

AsyncTaskDemoActivity _parentActivity;
int _counter;
int _maxCount;

public AsyncTaskDemo(AsyncTaskDemoActivity asyncTaskDemoActivity) {
    _parentActivity = asyncTaskDemoActivity;
}

@Override
protected void onPreExecute() {
    super.onPreExecute();
    _parentActivity._progressBar.setVisibility(ProgressBar.VISIBLE);
    _parentActivity._progressBar.invalidate();
}

@Override
protected String doInBackground(Integer... params) {
    _maxCount = params[0];
    for (_counter = 0; _counter <= _maxCount; _counter++) {
        try {
            Thread.sleep(1000);
            publishProgress(_counter);
        } catch (InterruptedException e) {
            // Ignore           
        }
    }
}

@Override
protected void onProgressUpdate(Integer... values) {
    super.onProgressUpdate(values);
    int progress = values[0];
    String progressStr = "Counting " + progress + " out of " + _maxCount;
    _parentActivity._textView.setText(progressStr);
    _parentActivity._textView.invalidate();
}

@Override
protected void onPostExecute(String result) {
    super.onPostExecute(result);
    _parentActivity._progressBar.setVisibility(ProgressBar.INVISIBLE);
    _parentActivity._progressBar.invalidate();
}

@Override
protected void onCancelled() {
    super.onCancelled();
    _parentActivity._textView.setText("Request to cancel AsyncTask");
}

}

এটি একটি পরীক্ষার মামলা। এখানে AsyncTaskDemoActivity মোডে AsyncTask পরীক্ষার জন্য ইউআই সরবরাহকারী একটি খুব সাধারণ ক্রিয়াকলাপ:

package kroz.andcookbook.test.threads.asynctask;
import java.util.concurrent.ExecutionException;
import kroz.andcookbook.R;
import kroz.andcookbook.threads.asynctask.AsyncTaskDemo;
import kroz.andcookbook.threads.asynctask.AsyncTaskDemoActivity;
import android.content.Intent;
import android.test.ActivityUnitTestCase;
import android.widget.Button;

public class AsyncTaskDemoTest2 extends ActivityUnitTestCase<AsyncTaskDemoActivity> {
AsyncTaskDemo _atask;
private Intent _startIntent;

public AsyncTaskDemoTest2() {
    super(AsyncTaskDemoActivity.class);
}

protected void setUp() throws Exception {
    super.setUp();
    _startIntent = new Intent(Intent.ACTION_MAIN);
}

protected void tearDown() throws Exception {
    super.tearDown();
}

public final void testExecute() {
    startActivity(_startIntent, null, null);
    Button btnStart = (Button) getActivity().findViewById(R.id.Button01);
    btnStart.performClick();
    assertNotNull(getActivity());
}

}

এই সমস্ত কোডটি ঠিকঠাকভাবে কাজ করছে, অ্যান্ড্রয়েড টেস্টিং ফ্রেমওয়ার্কের মাধ্যমে কার্যকর করা হলে অ্যাসিঙ্কটাস্ক এটির বিজ্ঞপ্তি পদ্ধতিগুলি গ্রহণ করে না except কোন ধারনা?

উত্তর:


125

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

আমি নিম্নলিখিত পদ্ধতিটি পেয়েছি, যা কাজ করেছিল এবং আমি এখনও এটি ব্যবহার করি। আমি ওয়েট-নোটিফিকেশনটি প্রয়োগ করতে কেবল কাউন্টডাউনল্যাচ সিগন্যাল অবজেক্টগুলি ব্যবহার করি (আপনি সিঙ্ক্রোনাইজড (লক) ব্যবহার করতে পারেন lock ... লক.নোটাইফাই ();} তবে এটি কুৎসিত কোডে ফলাফল করে)।

public void testSomething(){
final CountDownLatch signal = new CountDownLatch(1);
Service.doSomething(new Callback() {

  @Override
  public void onResponse(){
    // test response data
    // assertEquals(..
    // assertTrue(..
    // etc
    signal.countDown();// notify the count down latch
  }

});
signal.await();// wait for callback
}

4
কী Service.doSomething()?
পিটার আজতাই

11
আমি একটি অ্যাসিচটাস্ক পরীক্ষা করছি। এটি কি এবং ঠিক আছে, ব্যাকগ্রাউন্ড টাস্কটিকে কখনও বলা হয় না এবং এককালের জন্য চিরকাল অপেক্ষা করা হয় :(
আইএক্সএক্স

@ আইএক্সএক্স, আপনি কি task.execute(Param...)আগে ফোন করেছিলেন await()এবং প্রবেশ countDown()করেছিলেন onPostExecute(Result)? (দেখুন stackoverflow.com/a/5722193/253468 ) এ ছাড়া @PeterAjtai, Service.doSomethingমত ASYNC কল task.execute
TWiStErRob

কি সুন্দর এবং সহজ সমাধান।
ম্যাকিয়েজ বেমসাইক

Service.doSomething()আপনার পরিষেবা / অ্যাসিঙ্ক টাস্ক কলটি প্রতিস্থাপন করা উচিত। signal.countDown()আপনার প্রয়োগ করতে হবে এমন যে কোনও পদ্ধতিতে কল করতে বা আপনার পরীক্ষাটি আটকে যাবে তা নিশ্চিত করুন ।
ভিক্টর আর। অলিভিরা

94

আমি প্রচুর কাছাকাছি উত্তর পেয়েছি কিন্তু তাদের কোনওটিই সমস্ত অংশ সঠিকভাবে একত্রিত করে নি। সুতরাং আপনার JUnit পরীক্ষার ক্ষেত্রে একটি android.os.AsyncTask ব্যবহার করার সময় এটি একটি সঠিক বাস্তবায়ন।

 /**
 * This demonstrates how to test AsyncTasks in android JUnit. Below I used 
 * an in line implementation of a asyncTask, but in real life you would want
 * to replace that with some task in your application.
 * @throws Throwable 
 */
public void testSomeAsynTask () throws Throwable {
    // create  a signal to let us know when our task is done.
    final CountDownLatch signal = new CountDownLatch(1);

    /* Just create an in line implementation of an asynctask. Note this 
     * would normally not be done, and is just here for completeness.
     * You would just use the task you want to unit test in your project. 
     */
    final AsyncTask<String, Void, String> myTask = new AsyncTask<String, Void, String>() {

        @Override
        protected String doInBackground(String... arg0) {
            //Do something meaningful.
            return "something happened!";
        }

        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);

            /* This is the key, normally you would use some type of listener
             * to notify your activity that the async call was finished.
             * 
             * In your test method you would subscribe to that and signal
             * from there instead.
             */
            signal.countDown();
        }
    };

    // Execute the async task on the UI thread! THIS IS KEY!
    runTestOnUiThread(new Runnable() {

        @Override
        public void run() {
            myTask.execute("Do something");                
        }
    });       

    /* The testing thread will wait here until the UI thread releases it
     * above with the countDown() or 30 seconds passes and it times out.
     */        
    signal.await(30, TimeUnit.SECONDS);

    // The task is done, and now you can assert some things!
    assertTrue("Happiness", true);
}

4
সম্পূর্ণ উদাহরণ লেখার জন্য ধন্যবাদ ... এটি বাস্তবায়নে আমার অনেক ছোট সমস্যা হয়েছিল।
পিটার আজতাই

মাত্র 1 বছর পরে, এবং আপনি আমাকে বাঁচিয়েছেন। ধন্যবাদ বিলি ব্র্যাকেন!
ম্যাটটি

8
আপনি যদি পরীক্ষার ব্যর্থতা হিসাবে গণ্য করার সময়সীমা চান তবে আপনি এটি করতে পারেন:assertTrue(signal.await(...));
জেরেট মিল্লার্ড

4
আরে বিলি আমি এই বাস্তবায়নটি চেষ্টা করেছি কিন্তু রানটেষ্টঅনুআইথ্রেড পাওয়া যায় নি। পরীক্ষার কেসটি অ্যান্ড্রয়েড টেস্টকেস প্রসারিত করা উচিত বা এটি ক্রিয়াকলাপের ইনসার্টমেন্টেশনটেষ্টক্যাস 2 বাড়ানো দরকার?
ডগ রে

4
@ ডউগ্রে আমার একই সমস্যা ছিল - আপনি যদি ইনস্ট্রুমেন্টেশনটেষ্ট কেস বাড়ান তবে রানটেষ্টঅনুআইথ্রেড পাওয়া যাবে।
লুমিনায়ার

25

এটির সাথে মোকাবিলা করার উপায় হ'ল যে কোনও কোড যা একটি এসিঙ্কটাস্ককে অনুরোধ করে তা চালানো হয় runTestOnUiThread():

public final void testExecute() {
    startActivity(_startIntent, null, null);
    runTestOnUiThread(new Runnable() {
        public void run() {
            Button btnStart = (Button) getActivity().findViewById(R.id.Button01);
            btnStart.performClick();
        }
    });
    assertNotNull(getActivity());
    // To wait for the AsyncTask to complete, you can safely call get() from the test thread
    getActivity()._myAsyncTask.get();
    assertTrue(asyncTaskRanCorrectly());
}

ডিফল্টরূপে জুনিট মূল অ্যাপ্লিকেশন ইউআইয়ের চেয়ে পৃথক থ্রেডে পরীক্ষা চালায়। অ্যাসিঙ্কটাস্কের ডকুমেন্টেশন বলছে যে টাস্ক ইনস্ট্যান্স এবং () কার্যকর করার জন্য কলটি অবশ্যই মূল ইউআই থ্রেডে থাকতে হবে; এটি কারণ AsyncTask মূল থ্রেড Looperএবং MessageQueueএর অভ্যন্তরীণ হ্যান্ডলারের সঠিকভাবে কাজ করার জন্য নির্ভর করে ।

বিঃদ্রঃ:

আমি আগে @UiThreadTestপরীক্ষাটি মূল থ্রেডে চালিত করার জন্য পরীক্ষার পদ্ধতিতে ডেকরেটার হিসাবে ব্যবহার করার পরামর্শ দিয়েছিলাম তবে এটি অ্যাসিঙ্কটাস্ক পরীক্ষা করার পক্ষে ঠিক ঠিক নয় কারণ আপনার পরীক্ষার পদ্ধতিটি মূল থ্রেডে চলমান থাকাকালীন কোনও বার্তা প্রক্রিয়াকরণ করা হয় না মূল বার্তাকিউ - AsyncTask এর অগ্রগতি সম্পর্কে প্রেরিত বার্তাগুলি সহ, আপনার পরীক্ষা স্থগিত করে।


এটি আমাকে বাঁচিয়েছে ... যদিও আমাকে অন্য একটি থ্রেড থেকে "রানটেষ্টঅনউইথ্রেড" কল করতে হয়েছিল অন্যথায়, আমি পেয়েছি "মূল অ্যাপ্লিকেশন থ্রেড থেকে এই পদ্ধতিটি কল করা যায় না"
ম্যাথিউ

@ ম্যাথিউ আপনি কি ডেকরেটারের runTestOnUiThread()সাথে একটি পরীক্ষা পদ্ধতি থেকে ব্যবহার করছেন @UiThreadTest? যে কাজ করবে না। যদি কোনও পরীক্ষার পদ্ধতি না থাকে তবে @UiThreadTestএটি ডিফল্টরূপে তার নিজস্ব অ-প্রধান থ্রেডে চালানো উচিত।
অ্যালেক্স প্রেটজ্লাভ

4
উত্তরটি খাঁটি রত্ন। এটি আপডেটে জোর দেওয়ার জন্য পুনরায় কাজ করা উচিত, যদি আপনি সত্যিই প্রাথমিক উত্তরটি রাখতে চান তবে এটিকে কিছু ব্যাকগ্রাউন্ডের ব্যাখ্যা এবং একটি সাধারণ ক্ষতি হিসাবে রাখুন।
Snicolas

4
ডকুমেন্টেশন পদ্ধতিটি Deprecated in API level 24 বিকাশকারীকে
জানিয়েছে andঅ্যান্ড্রয়েড.com

4
অবচয়, InstrumentationRegistry.getInstrumentation().runOnMainSync()পরিবর্তে ব্যবহার করুন!
মার্কো 7757

5

যদি আপনি কলার থ্রেডে অ্যাসিঙ্কটাস্কটি কার্যকর করতে আপত্তি করেন না (ইউনিট পরীক্ষার ক্ষেত্রে ভাল হওয়া উচিত) তবে আপনি https://stackoverflow.com/a/6583868/1266123 তে বর্ণিত হিসাবে বর্তমান থ্রেডে এক্সিকিউটর ব্যবহার করতে পারেন

public class CurrentThreadExecutor implements Executor {
    public void execute(Runnable r) {
        r.run();
    }
}

এবং তারপরে আপনি আপনার ইউনিট পরীক্ষায় আপনার এসিঙ্কটাস্কটি চালান

myAsyncTask.executeOnExecutor(new CurrentThreadExecutor(), testParam);

এটি কেবল হানিকম্ব এবং উচ্চতরর জন্য কাজ করছে।


এটি উপরে যাওয়া উচিত
স্টেফানটো

5

আমি অ্যান্ড্রয়েডের জন্য যথেষ্ট সংঘবদ্ধতা লিখেছি এবং কীভাবে এটি করতে হয় তা ভাগ করে নিতে চাই।

প্রথমে, এখানে অপেক্ষাকারী এবং অপেক্ষা করার জন্য দায়বদ্ধ সহায়ক শ্রেণি রয়েছে। বিশেষ কিছু না:

সিঙ্ক্রোনাইজডালকার

public class SyncronizeTalker {
    public void doWait(long l){
        synchronized(this){
            try {
                this.wait(l);
            } catch(InterruptedException e) {
            }
        }
    }



    public void doNotify() {
        synchronized(this) {
            this.notify();
        }
    }


    public void doWait() {
        synchronized(this){
            try {
                this.wait();
            } catch(InterruptedException e) {
            }
        }
    }
}

এর পরে, একটি পদ্ধতি দিয়ে ইন্টারফেস তৈরি করতে দিন যা AsyncTaskকাজ শেষ হওয়ার পরে কল করা উচিত। অবশ্যই আমরা আমাদের ফলাফলগুলি পরীক্ষা করতে চাই:

টেস্টটাস্কিটিফ

public interface TestTaskItf {
    public void onDone(ArrayList<Integer> list); // dummy data
}

এর পরে আমাদের টাস্কের কিছু কঙ্কাল তৈরি করতে দেয় যা আমরা পরীক্ষা করব:

public class SomeTask extends AsyncTask<Void, Void, SomeItem> {

   private ArrayList<Integer> data = new ArrayList<Integer>(); 
   private WmTestTaskItf mInter = null;// for tests only

   public WmBuildGroupsTask(Context context, WmTestTaskItf inter) {
        super();
        this.mContext = context;
        this.mInter = inter;        
    }

        @Override
    protected SomeItem doInBackground(Void... params) { /* .... job ... */}

        @Override
    protected void onPostExecute(SomeItem item) {
           // ....

       if(this.mInter != null){ // aka test mode
        this.mInter.onDone(data); // tell to unitest that we finished
        }
    }
}

শেষ পর্যন্ত - আমাদের ইউনিট শ্রেণি:

টেস্টবিল্ডগ্রুপটাস্ক

public class TestBuildGroupTask extends AndroidTestCase  implements WmTestTaskItf{


    private SyncronizeTalker async = null;

    public void setUP() throws Exception{
        super.setUp();
    }

    public void tearDown() throws Exception{
        super.tearDown();
    }

    public void test____Run(){

         mContext = getContext();
         assertNotNull(mContext);

        async = new SyncronizeTalker();

        WmTestTaskItf me = this;
        SomeTask task = new SomeTask(mContext, me);
        task.execute();

        async.doWait(); // <--- wait till "async.doNotify()" is called
    }

    @Override
    public void onDone(ArrayList<Integer> list) {
        assertNotNull(list);        

        // run other validations here

       async.doNotify(); // release "async.doWait()" (on this step the unitest is finished)
    }
}

এখানেই শেষ.

আশা করি এটি কারও সাহায্য করবে।


4

আপনি যদি doInBackgroundপদ্ধতি থেকে ফলাফলটি পরীক্ষা করতে চান তবে এটি ব্যবহার করা যেতে পারে । onPostExecuteপদ্ধতিটি ওভাররাইড করুন এবং সেখানে পরীক্ষা করুন। অ্যাসিঙ্কটাস্কের কাউন্টডাউনল্যাচ সম্পূর্ণরূপে অপেক্ষা করার জন্য অপেক্ষা করুন। latch.await()অপেক্ষা করছে 0 1 থেকে কাউন্টডাউন রান (যা আরম্ভের সময় সেট করা হয়) (যার দ্বারা সম্পন্ন করা হয় পর্যন্ত countdown()পদ্ধতি)।

@RunWith(AndroidJUnit4.class)
public class EndpointsAsyncTaskTest {

    Context context;

    @Test
    public void testVerifyJoke() throws InterruptedException {
        assertTrue(true);
        final CountDownLatch latch = new CountDownLatch(1);
        context = InstrumentationRegistry.getContext();
        EndpointsAsyncTask testTask = new EndpointsAsyncTask() {
            @Override
            protected void onPostExecute(String result) {
                assertNotNull(result);
                if (result != null){
                    assertTrue(result.length() > 0);
                    latch.countDown();
                }
            }
        };
        testTask.execute(context);
        latch.await();
    }

-1

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

একটি লাইব্রেরি রয়েছে যা পরীক্ষার প্রক্রিয়াটি সহজ করে দেয় AsyncTask। উদাহরণ:

@Test
  public void makeGETRequest(){
        ...
        myAsyncTaskInstance.execute(...);
        AsyncTaskTest.build(myAsyncTaskInstance).
                    run(new AsyncTest() {
                        @Override
                        public void test(Object result) {
                            Assert.assertEquals(200, (Integer)result);
                        }
                    });         
  }       
}

মূলত, এটি আপনার অ্যাসিঙ্কটাস্ক চালায় এবং ফলাফলটি ডাকার পরে এটির ফলাফলটি পরীক্ষা করে postComplete()

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.