AsyncTask.onPostExecute () никогда не вызывается в ServiceTestCase - PullRequest
3 голосов
/ 20 мая 2011

Я пишу модульный тест для службы, используя ServiceTestCase.

Служба в основном выполняет AsyncTask, которая выполняет некоторую работу, а затем выполняет что-то еще в onPostExecute ().

Служба работает должным образом, когда я запускаю и отлаживаю ее на (виртуальном) устройстве.

Но в тесте, расширяющем ServiceTestCase, я попадаю только в doInBackground (). Как только методы возвращаются, onPostExecute () никогда не вызывается. Я даю тесту sleep (), чтобы AsyncTask успел завершить свою работу.

Это упрощенная услуга:

public class ServiceToTest extends Service {
    private AtomicBoolean busy = new AtomicBoolean(false);

    @Override
    public IBinder onBind(final Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(final Intent intent, final int flags,
        final int startId) {
        this.handleCommand();
        return START_NOT_STICKY;
    }

    /**
    * Workaround for http://code.google.com/p/android/issues/detail?id=12117
    */
    @Override
    public void onStart(final Intent intent, final int startId) {
        this.handleCommand();
    }

    public void handleCommand() {
        new TaskToTest().execute();
    }

    public boolean isBusy() {
        return busy.get();
    }

    private class TaskToTest extends AsyncTask<Boolean, Void, TestInfo> {
        @Override
        protected void onPreExecute() {
            busy.set(true);
        }

        @Override
        protected TestInfo doInBackground(final Boolean... args) {
            return null;
        }

        @Override
        protected void onPostExecute(final TestInfo info) {
            busy.set(false);
        }
    }
}

Это тест для него:

public class ServiceTest extends ServiceTestCase<ServiceToTest> {
    public ServiceTest() {
        super(ServiceToTest.class);
    }

    public void testIsBusy() throws InterruptedException {
        startService(new Intent("this.is.the.ServiceToTest"));  
        ServiceToTest serviceToTest = this.getService();
        assertTrue(serviceToTest.isBusy());
        Thread.sleep(10000);
        assertFalse(serviceToTest.isBusy());
    }
}

Я полагаю, что среда, предоставляемая ServiceTestCase, несколько ограничена, так что это не работает, но могу ли я что-нибудь сделать, чтобы она заработала?

Cheers, Торстен

Ответы [ 4 ]

1 голос
/ 20 мая 2011

Так что просто следите за тем, как я заставил его работать с информацией, предоставленной dmon.

Я изменил свой тест на следующее:

public class ServiceTest extends ServiceTestCase {</p> <pre><code>public ServiceTest() { super(ServiceToTest.class); } public void testIsBusy() throws InterruptedException { // Starts the service and asserts that onPreExecute() was called ServiceTestThread serviceTestThread = new ServiceTestThread(); serviceTestThread.start(); // Wait for the service to start and complete doInBackground() // TODO Implement something smarter than this... Thread.sleep(1000); // Assert that onPostExecute() was called assertFalse(serviceTestThread.serviceToTest.isBusy()); } private class ServiceTestThread extends Thread { ServiceToTest serviceToTest; public void run() { Looper.prepare(); startService(new Intent("this.is.the.ServiceToTest")); serviceToTest = getService(); assertTrue(serviceToTest.isBusy()); Looper.loop(); } }

}

Сейчас я посмотрю, чтобы сделать этот ServiceTestThread более общим, чтобы его можно было повторно использовать.

Torsten

1 голос
/ 20 мая 2011

Проблема в том, что ваш фоновый поток ожидает "живого" пользовательского интерфейса, вам нужно вызвать Looper.prepare() и Looper.loop().Это лучше объяснить на этой странице .

0 голосов
/ 03 декабря 2013

У меня была такая же проблема при попытке привязки к сервису из потока тестового бегуна, а не из потока пользовательского интерфейса.Попробуйте вызвать startService из потока пользовательского интерфейса.

public void testIsBusy() throws Exception {
  final CountDownLatch latch = new CountDownLatch(1);

  new Handler(Looper.getMainLooper()).post(new Runnable() {
    @Override
    public void run() {
      startService(new Intent("this.is.the.ServiceToTest"));  
      ServiceToTest serviceToTest = this.getService();
      assertTrue(serviceToTest.isBusy());
      Thread.sleep(10000);
      assertFalse(serviceToTest.isBusy());
      latch.countDown();
    }
  });

  latch.await(5, TimeUnit.SECONDS);
}
0 голосов
/ 09 июня 2012

Не уверен, полезно ли это кому-либо еще, но это была моя попытка абстрагировать ответ Тортенса и сделать его более пригодным для повторного использования.

    private synchronized boolean getWaitFlag()
    {
        return _waitFlag;
    }

    private boolean _waitFlag;

    private synchronized void setWaitFlag(boolean value)
    {
        _waitFlag = value;
    }

    private void waitForCompletionFlag() throws InterruptedException
    {
        Calendar cal = Calendar.getInstance();
        while (getWaitFlag() == false)
        {
            Thread.sleep(10);
            if (Calendar.getInstance().getTimeInMillis() - cal.getTimeInMillis() > 1000) // Wait at most 1 second
            {
                Log.e("timeout", "timed out waiting to complete task");
                break;
            }
        }
    }

private abstract class EmulatedUI extends Thread
    {
        public abstract void doWork();

        public void run()
        {
            Looper.prepare();
            doWork();
            Looper.loop();
        }
    }

public void testSomething() throws InterruptedException
{
    EmulatedUI thread = new EmulatedUI() {

        @Override
        public void doWork()
        {
            _objectToTest.someAsyncCall(new WorkCompletedCallback() {

                        @Override
                        public void onComplete()
                        {
                                   // could possibly assert things here
                            setWaitFlag(true);
                        }
                    });

        }
    };
    thread.start();
    waitForCompletionFlag();
    // assert things here since you know the async task has completed.
}
...