我正在使用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 :(得分:1)
问题是你的后台线程正在等待用户界面“活着”,你需要拨打Looper.prepare()
和Looper.loop()
。最好在this page中解释。
答案 1 :(得分: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();
}
}
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
答案 2 :(得分: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.
}
答案 3 :(得分: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);
}