AsyncTask.onPostExecute() 在 ServiceTestCase 中永远不会被调用

发布于 2024-11-08 17:20:08 字数 2080 浏览 0 评论 0原文

我正在使用 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 提供的环境有些限制,所以这不起作用,但是有什么办法吗?无论如何,我能做些什么来让它发挥作用吗?

干杯, 托斯顿

I am writing a unit test for a service using ServiceTestCase.

The service basically executes an AsyncTask that does some work and then does something else in onPostExecute().

The service works as expected when I run and debug it in a (virtual) device.

But in the test extending ServiceTestCase, I only get into doInBackground(). Once the methods returns, onPostExecute() never gets called. I am letting the test sleep() so the AsyncTask has time to complete its work.

This is the simplified service:

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);
        }
    }
}

An this is the test for it:

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());
    }
}

I suppose that the environment provided by ServiceTestCase is somewhat limited so this doesn't work, but is there anything I can do to make it work anyway?

Cheers,
Torsten

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

撩动你心 2024-11-15 17:20:08

问题是您的后台线程正在等待 UI“活动”,您需要调用 Looper.prepare() 和 Looper.loop() 。 此页面对此进行了更好的解释。

The problem is that your background thread is waiting for the UI to be "alive", you need to call Looper.prepare() and Looper.loop(). It's better explained in this page.

恏ㄋ傷疤忘ㄋ疼 2024-11-15 17:20:08

所以只是为了跟进我如何让它与 dmon 提供的信息一起工作。

我将测试更改为以下内容:


公共类 ServiceTest 扩展 ServiceTestCase {

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 更加通用,以便可以重用。

托斯顿

So just to follow up on how I got it to work with the info provided by dmon.

I changed my test to the below:


public class ServiceTest extends ServiceTestCase {

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();
    }

}

}

I'll see now to make this ServiceTestThread more generic so it can be reused.

Torsten

靑春怀旧 2024-11-15 17:20:08

不确定这对其他人是否有用,但这是我尝试抽象 Tortens 答案并使其更可重用。

    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.
}

Not sure if this is useful to anybody else, but this was my attempt at abstracting Tortens answer and making it more reusable.

    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.
}
把昨日还给我 2024-11-15 17:20:08

当我尝试从测试运行程序线程而不是 ui 线程绑定到服务时,我遇到了同样的问题。尝试从 ui 线程调用 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);
}

I had the same problem when attempting to bind to a service from the test runner thread, rather than the ui thread. Try calling startService from the ui thread.

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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文