3

我正在爲使用ServiceTestCase的服務編寫單元測試。AsyncTask.onPostExecute()永遠不會在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提供的環境是比較有限所以這並未不工作,但有什麼我可以做的,無論如何使它工作?

乾杯, 託斯滕

+0

請張貼代碼... – neteinstein 2011-05-19 22:11:50

+0

你檢查,如果你從DoInBackground返回一個非空的結果會發生什麼? – Torid 2011-05-19 23:51:30

+0

是的,我試過了,它沒有任何區別 – 2011-05-20 11:57:05

回答

1

的問題是,你的後臺線程等待UI是「活着」,你需要調用Looper.prepare()Looper.loop()。最好在this page中解釋。

+0

非常感謝dmon - 這幫助我實現了它的功能。對不起,我不能說「有用」,因爲我還沒有足夠的聲望呢... – 2011-05-20 11:58:36

+0

沒問題,很高興聽到它爲你工作。 – dmon 2011-05-20 12:17:09

1

所以只是爲了跟進我如何使它與dmon提供的信息一起工作。

我改變了我的測試,下面:

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

} 

}

我現在看到的,使這個ServiceTestThread更通用的,所以它可以重複使用。

託斯滕

0

不知道這是對別人有用的,但是這是我在抽象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. 
} 
+0

需要在某處調用Looper.quit()或回收EmulatedUI線程的方法。 – 2013-10-10 23:08:07

0

我嘗試從測試運行器線程而不是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); 
} 
相關問題