안드로이드 unit test 에서 async task 결과를 테스트하기 by 오리대마왕

안드로이드 앱의 코어 부분을 만들고 있다. UI쪽이 아니라 단위 테스트를 해 보려고 했는데, 일반적인 호출은 HelloWorldTest 예제를 보고 무리없이 성공했으나, async task를 이용한 처리 결과 확인엔 실패했다.

AsyncTask를 직접 생성해서 호출하는 경우라면 AsyncTask.get() 메서드 등을 써서 쉽게 해결할 수 있을 듯 하나, 내가 만드는 구조에선 AsyncTask는 모두 뒷단에 숨겨져 있고, 앞에선 단지 callback만 넘기는 형태이다. 대충 이런 구조다.

final Data data = new Data();

Callback a = new Callback() {
onPrepare() { data.val = 1;}
onSuccess() { data.val = 2;}
onFail() { data.val = 3; }
}

Manager.getInstance().doSomething(a);
assertEquals( data.val, 2 );


분명히 별다른 이상이 없는데 처음엔 onPrepare() 의 결과도 data에 제대로 반영되어 있지 않았다.
한참 고민하다, 아! 난 지금 비동기 작업을 테스트하고 있었지! 하는 생각이 들었다. 결국 doSomething() 이 제대로 완료되지도 않은 상태에서 assert 구문이 실행되니 당연히 실패하지.

그래서 무식한 방법으로 doSomething 과 assert 사이에 Thread.sleep 을 주었다. 이렇게 하니 onPrepare() 의 결과는 제대로 반영이 되었으나, onSuccess() 까지 가지 않는다. sleep을 아무리 주어도 매한가지다.

가만히 생각해보니 onSuccess 는 처리가 다 끝난 다음에 UIThread 상에서 실행되도록 handler에 등록되어 동작한다. 그런데 현재 실행되는 unit test의 쓰레드와 onSuccess 가 실행될 쓰레드가 같은 쓰레드이다. 따라서 현재의 구현에선 unit test의 모든 작업이 다 끝난 다음에 비로서 onSuccess 가 실행되니 그 사이에 assert 가 끼어들 여지가 없게 된다.

뭐 이런 저런 생각과 실험을 다 해 보았지만, 이건 답이 없는거라. 둘이 다른 쓰레드라면 모르겠는데 handler를 통해 한 쓰레드에서 돌아가는 이상 해결책이 없다.

한참 고민하다 stackoverflow를 뒤지니 딱 정답이 나오네. InstrumentationTestCase.runTestOnUiThread() 메서드를 실행하면 unit test가 실행되는 쓰레드와 대상 작업을 실행할 쓰레드를 분리할 수 있다. 따라서 async task로 수행할 작업은 ui 쓰레드에서, 유닛테스트의 작업은 기본 테스트 쓰레드에서 실행하면 된다. 이 경우 ui 쓰레드가 완료될 때 까지 유닛테스트 쓰레드를 재워놓으면되는데, CountDownLatch 를 썼다. 그렇게 만든 코드는 대략 다음의 형태가 된다.


public class AsyncTaskTest extends InstrumentationTestCase {

public void testAsyncTask() {

final Data data = new Data();
final CountDownLatch signal = new CountDownLatch(1); // countdown 1을 기다리는 랫치

Callback a = new Callback() {
onPrepare() { data.val = 1; }
onSuccess() { data.val = 2; signal.countDown();} // 작업이 끝나면 1을 내려 유닛테스트 쓰레드가 진행되게 한다.
onFail() { data.val = 3; signal.countDown(); } // 실패할 수도 있으니..
}

runTestOnUiThread( new Runnable() {
public void run() {
Manager.getInstance().doSomething(a);

}
} );
signal.await(); // 랫치 값이 0이 될 때 까지 대기 상태
assertEquals( data.val, 2 ); //결과 확인

}
}


물론 CountDownLatch 를 제대로 쓰기 위해선 고민해야 할 부분이 많지만, test 코드니 대충 저 정도로 해 두자.

핑백

덧글

  • 2011/04/07 11:02 # 삭제 비공개

    비공개 덧글입니다.
※ 로그인 사용자만 덧글을 남길 수 있습니다.