티스토리 뷰

Mobile/Android

Testing. Instrumentation

out of coding 2014. 2. 6. 17:35

일반적으로 안드로이드 테스팅 환경은 jUnit를 이용하여서 사용할수 있도록 하여준다.


안드로이드 테스팅 환경의 핵심 키 포인트

  • 안드로이드 시스템 객체에 접근 가능하도록 jUnit 프레임워크를 확장.
  • 테스트 중인 어플리케이션을 컨트롤 하고 상태를 확인 할 수 있도록, Instrumentation 프로임워크를 제공
  • Mock 버전의 안드로이드 시스템 객체를 제공
  • Instrumentation 프레임워크를 사용하거나 사용하지 않는 방식으로, 하나의 테스트 혹은 여러 테스트 집합(Test Suite)를 실행할 수 있는 툴을 제공
  • 테스트와 테스트 프로젝트를 손쉽게 관리 할 수 있도록, Eclipse ADT 플러그인과 커맨드 라인 도구를 제공


Instrumentation 프레임워크

  • 안드로이드 테스팅 환경의 가장 핵심 요소
  • Instrumentation 프레임워크를 이용하면, 어플리케이션 테스트를 정교하게 할수 있다.
  • (예) 메인 어플리케이션이 시작되기 전에 Mock Context 생성하여 사용하거나, 어플리케이션의 생명주기를 다양하게 컨트롤 하거나 UI 이벤트를 전달하거나, 실행중인 어플리케이션의 상태 정보를 확인 할수 있다.
  • 작성한 테스트 어플리케이션은 테스트 어플리케이션의 메니페스트 파일에 명시된 <instrumentation> 엘리먼트를 통해, 테스트 대상 어플과 연결이 된다. 이 엘리먼트는 속성 값으로 테스트 대상이 되는 어플리케이션의 패키지 이름과 어떤 방식으로 테스트 어플리케이션이 동작해야하는지에 대해서 명시가 된다.


또한 안드로이드 테스트 어플리케이션은 그것 자체로도 하나의 안드로이드 어플리케이션이다.


안드로이드 테스팅과 관련되어 다음과 같은 참고 자료가 제공된다.


Java를 통해 테스트 케이스와 테스트 어플리케이션을 작성할 수 있도록, 안드로이드는 jUnit에 기반한 테스트 API를 제공
작성한 테스트가 테스트 중인 어플리케이션의 상태 정보와 런타임 오브젝트에 접근할 수 있도록 Instrumentation 프레임워크를 포함


JUnit Test Case Classes
  • 테스트 API에 포함되어 있는 몇몇 클래스는 Instrumentation 프레임워크를 사용하지 않고, jUnit의 TestCase를 상속받아 구현되었다. 이런 클래스들은 현재 테스트중인 어플리케이션의 시스템 객체들에 접근 할 수 있는 메서드를 포함하고 있으며, 이를 이용하여 개발자들은 특정 어플리케이션의 리소스나 파일 데이터베이스등등을 잠글수 있다. (다른 객체가 접근하지 못함. LOCK)
  • 이 클래스들의 베이스 클래스는 AndroidTestCase이며, 일반적으로 테스트 할 특정 어플리케이션의 구성요서와 대응되는 하위 클래스를 사용한다.
  • 하위 클래스
    1. ApplicationTestCase : 전체 응용 프로그램을 테스트하는 클래스. 개발자는 Mock Context를 어플리케이션에 적용하거나, 어플리케이션이 시작하기전에 테스트 인자들을 설정하거나, 어플리케이션이 종료되었지만 완전히 메모리 상에서 사라지기 전에 상태를 확인할 수 있다.
    2. ProviderTestCase2 : 하나의 ContentProvider를 테스트 하는데 사용 가능한 클래스, MockContentResolver와 IsolatedContext를 사용하기 때문에, 안드로이드 OS의 다른 요소와 독립적으로 테스트가 가능
    3. ServiceTestCase : Service를 테스트하기 위한 클래스, Mock Context 또는 Mock Application 를 사용하거나 둘다 사용할수 있으며, 그렇지 않으면 안드로이드 온전한 Context와 MockApplication을 제공하도록 할수도 있다.

Instrumentation Test Case Classes

  • Activity를 테스트 하는데 사용되는 API들은 jUnit의 TestCase 클래스를 상속받고, 동시에 Instrumentation 프레임워크를 사용한다. Instrumentation을 이용해서, 안드로이드는 현재 테스트 중인 어플리케이션에 UI 이벤트를 전달하는 방식으로 자동화된 UI테스트를 진행할 수 있으며, Activity가 시작되는것을 컨트롤할수 있고, Activity 생명주기에 따른 상태를 모니터링 할 수도 있다.
  • 기본 클래스는 InstrumentationTestCase이며, 이 클래스를 상속받은 모든 하위 클래스들은 테스트중인 어플리케이션에 UI 이벤트를 전달할 수 있다. 또한 Mock Intent를 적용하는 기능도 갖초고 있다.
  • 하위 클래스
    1. ActivityTestCase : Activity를 테스트하는데 사용되는 기본 클래스
    2. SingleLaunchActivityTestCase : 하나의 Activity를 테스트하는데 편리하게 사용하는 클래스, 메서드 호출 마다 매번 setUp()과 tearDown()이 호출되는 대신, 딱 한번만 호출됨. 테스트에 사용되는 모든 테스트 케이스가 동일한 Activity에 대하여 수행되는 경우에 사용한다.
    3. SyncBaseInstrumentation : Content Provider와 동기화를 테스트하는데 사용되는 클래스, 동기화 테스트를 시작하기 전에, 현재 진행중인 동기화 과정을 취소하는데 Instrumentation 프레임워크가 사용된다.
    4. ActivityUnitTestCase : 하나의 Activity를 독립적으로 테스트할 때 사용. Mock Context 혹은 Activity에 관한 Unit테스트를 수행하는데 사용되도록 고안되었음. jUnit Test Case Classes에서 설명된 클래스들의 Activity 버전
    5. ActivityInstrumentationTestCase2 : 일반적인 시스템 환경하에서 하나의 Activity를 테스트한다. 따라서, 개발자는 Mock Context를 사용할수 없으며, 대신 Mock Intent는 사용 가능함. 또한 테스트 케이스를 해당 어플리케이션의 UI스레드(메인 스레드)에서 수행할수 있으며, 키혹은 터치 이벤트를 어플리케이션 UI 쪽에 전달할 수 있다.


Assert Classes

  • 테스트에서 assert()를 호출할 때 사용되는 jUnit Assert 클래스를 확장하여 제공한다
  • MoreAsserts : 강력한 assert 함수들을 지원한다. (ex) 조건식에 정규식을 사용할 수 있는  assertContationsRegex()메서드가 제공됨
  • ViewAsserts : 안드로이드 View에 관한 유용한 assert 메서드들을 지원한다. 예를 들어 assertHasScreenCoordinates() 메서드는 특정 View와 화면상의 특정 위치에 있는지를 체크한다. 이러한 assert 메서드들은 UI구조에 관한 테스트를 보다 쉽게 해준다.

Mock Object Classes(모의 객체 클래스)
  • 안드로이드는 어플리케이션, Context, Content Resolver, 리소스와 같은 시스템 오브젝트에 관한 Mock 오브젝트(모의 객체)를 만드는데 사용되는 클래스들을 제공한다. 또한 특정 테스트 클래스에 대해서는 Mock Intent를 만들기 위한 메서드도 제공해준다. 이러한 Mock 오브젝트는 실제 객체를 만드는 것보다 쉽기 때문에, 특정 시스템 오브젝트에 의존성이 있는 테스트를 수행하고자 할 때 실제 객체대신해 사용하기 좋다. 이 클래스들은 android.test와 android.test.mock 패키지안에 위치한다.
  • 하위 클래스
    1. IsolatedContext : 어플리케이션이 독립적으로 수행될 수 있도록, 온전한 Context객체 대신 사용할 수 있다. 이 클래스는 안드로이드 OS 단에서 Context와 커뮤니케이션하고자 할 때 필요로하는 기능을 만족하도록 구현되어 있다. UI 테스트용
    2. RenamingDelegatingContext : 기존에 존재하는 온전한 Context를 대신하여, 대부분의 기능을 수행한다. 단 Context에서 기본으로 사용되는 파일과 데이터베이스 이름을 변경할 수 있다. 즉, 일반적인 상황에서 테스트용 이름을 갖는 파일이나 데이터베이스를 사용하도록 할 수 있으며, 파일이나 데이터베이스 관련된 기능을 테스트하고자 할 때 사용된다.
    3. MockApplication, MockContentResolver, MockContext, MockDialogInterface, MockPackageManager, MockResources : 테스트에 사용할 Mock 오브젝트를 만드는데 사용되는 클래스들. 이 클래스들은 해당 오브젝트를 관리하는데 사용되는 메서드만으르 노출하고 있다. 노출된 메서드들은 기본적으로 Exception을 던지도록 구현되어 있다. 테스트 대상이되는 어플리케이션에서 해당 메서드를 호출하는 경우 개발자는 테스트 중에 예외가 발생하지 않도록, 해당 메서드를 오버라이드하여 적절하게 구현해 주어야 한다.

Instrumentation TestRunner
  • 안드로이드는 Instrumentation를 이용하여 테스트를 수행할 때 사용할 수 있는 InstrumentationTestRunner라는 커스텀 클래스를 제공한다. 이 클래스는 테스트중인 어플리케이션을 컨트롤하고, 테스트 어플리케이션과 메인 어플리케이션을 동일한 프로세스에서 작동시키며, 테스트 결과를 적절한 장소로 출력한다. Instrumentation을 이용하여, InstrumentationTestRunner는 전체적인 테스트 환경을 테스트가 진행중인 상황에서 컨트롤 할 수 있다. 여러분은 테스트 클래스 자체적으로 Instrumentation을 사용하지 않고 있더라고, 이 클래스를 사용할수 있다.
  • 테스트 어플리케이션을 실행할 때, 우선 Activity Manager가 실행된다. Activity Maager는 Test Runner를 시작하고 컨트롤 하기 위해 Instrumentation 프레임워크를 사용한다. 테스트 대상 어플리케이션이 이미 실행 중인 경우, 이를 종료하고, 테스트 어플리케이션과 메인 어플리케이션을 동일한 프로세스 상에서 다시 실행한다. 따라서, 어플리케이션은 메인 어플리케이션과 직접적으로 연결되어 다양한 테스트를 수행할 수 있다.
  • 만일 Eclipse ADT를 이용하여 개발을 한다면, 간단한 UI를 통해, 개발자는 테스트하려는 어플리케이션 패키지 이름을 설정하고, InstrumentationTestRunner를 사용하거나, 혹은 다른 TestRunner를 사용하도록 설정 할 수 있다. ADT 플러고인은 자동으로 적절한 속성값을 갖는 <instrumentation>엘리먼트를 테스트 어플리케이션의 매니페스트 파일에 추가한다. Eclipse는 테스트 어플리케이션을 자동으로 실행하며, 테스트 결과를 jUnit View로 출력한다.
  • 만약 커맨드라인에서 작업하는것을 선호한다면, Ant와 안드로이드 툴을 사용하여, 테스트 프로젝트를 설정 할 수 있다. Instrumentation을 이용하여 테스트를 수행하기 위해, Android Debug Bridge(ADB)를 통해 Activity Mananger에 접근할 수 있으며, 테스트 결과는 STDOUT(표준 출력)으로 출력된다.


Working in the Test Environment

  • 각각의 테스트는 안드로이드 프로젝트와 같은 구조로 구성된 하나의 프로젝트이다. 테스트 프로젝트는 메니페스트 파일을 통해 테스트 하려는 어플리케이션과 연결된다.
  • 각 테스트 어플리케이션은 하나 이상의 테스트 케이스 클래스를 포함한다. 각 클래스는 테스트 항목을 정의한 메소드를 가지게 된다. 개발자가 테스트 어플리케이션을 시작하면, 테스트 대상 어플리케이션이 동일한 프로세스상에 로드되며, 테스트 케이스 클래스에 정의된 메소드들이 호출된다.
  • 테스팅에 사용되는 도구와 절차는 개발환경에 따라 달라진다. 이클립스를 사용한다면 ADT 플러그인을 이용해 테스트 케이스를 개발하고 수행할 수 있으며, 다른 IDE라면, 커맨드라인을 이용하길 바란다.

Working with Test Projects
  • 안드로이드 어플리케이션 테스트를 시작하기 위하여, 개발자는 안드로이드 툴을 이용하여 테스트 프로젝트를 생성한다. 테스트 프로젝트는 필요한 파일, 디렉토리등을 갖추고 있으며, 테스트 대상이 되는 어플리케이션과 메니페스트 파일로 연결되어 있다.

Working with Test Case Classes
  • 테스트 어플리케이션은 하나 이상의 테스트 케이스 클래스를 포함. 테스트하고자 하는 어플리케이션 구성요소와 테스트 방식에 맞는 테스트 케이스 클래스를 선택해야한다. 각각의 테스트 클래스들은 특정한 종류의 어플리케이션 컴포넌트를 테스트하는데 사용되도록 디자인되어 있다.
  • 어떤 어플리케이션 구성요소는 대응되는 하나 이상의 테스트 케이스 클래스를 갖는다. 예를 들어 Activity를 위해서는 ActivityInstrumentationTestCase2 또는 ActivityInstrumentationTestCase 클래스를 사용할 수 있다.
  • ActivityInstrumentationCase2는 기능 테스트를 하는데 사용되도록 설계되었으며, 정상적인 시스템 환경하에서 테스트를 수행한다. 개발자는 Mock Intent를 사용할 수 있지만, Mock Context를 사용할 수는 없다. 즉, 개발자가 해당 Activity가 필요로 하는 시스템 의존적인 부분을 임의로 대체 할 수 없다.
  • 반면에 ActivityUnitTestCase는 Unit테스트를 위해 설계되었으며, 고립된 시스템 환경하에서 테스트를 수행한다. 개발자는 해당 Activity가 갖는 의존성을 임의로 대체할 수 있다. 대신 Activity는 시스템 환경과 고립되어 작동하며 다른 Activity들과 상호작용 할 수 없다.
  • 일반적으로, 특정 Activity가 다른 안드로이드 요소들과 잘 상호작용하는지를 테스트하기 위해서는 ActivityInstrumentationTestCase2를, 회귀분석(Regression) 테스트를 수행하는데는 ActivityUnitTestCase를 사용

Working with Test Methods
  • 각각의 테스트 케이스 클래스는 개발자가 테스트 환경을 설정하고 테스트 중인 어플리케이션을 컨트롤 할 수 있는 메서드를 제공한다. 예를 들어, 모든 테스트 케이스 클래스는 개발자가 테스트에 앞서 기본적인 작업을 수행할 수 있는 jUnit의 setUp() 메서드를 지원한다. 또한, 테스트 메서드를 테스트 케이스 클래스에 추가할 수 있다. 추가된 메서드들은 테스트 어플리케이션을 실핼 할 때마다 한번씩 수행된다. 개발자가 setUp()메서드를 오버라이드해 두었다면, 각각의 메서드가 수행되기 전에 setUp()메서드가 호출된다. 이와 유사하게, tearDown()메서두는 각각의 테스트가 수행된 후에 호출 된다.
  • 테스트 케이스 클래스는 테스트 중인 컴포넌트를 시작하거나 종료할 수 있는 메서드를 제공한다. 예를 들어, 개발자는 getActivity() 메서드를 호출하여 Activity를 시작할 수 있다. 필요에 따라 getActivity()를 전체적인 테스트 케이스 중에 한번만 호출할 수도 있고, 원한다면 테스트 중 finish() 메소드를 이용해서 Activity를 종료하고, 필요할 때, 다시금 getActivity() 메서드를 호출해 재시작 할 수도 있다.


Running Tests and Seeing the Results

  • 개발자는 테스트 프로젝트를 생성하고, Instrumentation과 Activity Manager를 통해 테스트를 수행한다. Activity Manager는 Test Runner의 이름과 테스트 대상이 되는 어플리케이션의 패키지 이름을 전달 받아, 테스트 어플리케이션을 실행하고, 이미 실행중인 메인 어플리케이션 (테스트 대상이 되는)을 종료하고, 테스트 어플리케이션과 동일한 프로세스 상에 해당 어플리케이션을 재시작한다. 그 후에, 테스트 어플리케이션의 첫 번째 테스트 케이스 클래스에게 컨트롤 권한을 넘겨준다. Test Runner는 권한을 넘겨받아, 테스트 중인 어플리케이션을 대상으로 테스트 메서드를 실행한다.
  • 만일 Eclipse를 이용한다면 테스트 결과는 jUnit View 패널에 표시되며, 커맨드라인을 이용하면 STDOUT에 테스트 결과가 출력된다.

Appendix: UI Testing Notes

  • 이어지는 섹션에서는 어플리케이션 UI를 테스트하는데 유용한 팁, 좀 더 구체적으로 말하자면, 터치 스크린 이벤트, 키보드 이벤트, 화면 잠금 해체와 같이 어플리케이션 UI 스레드(메인 스레드)에서 발생하는 액션들을 처리하는 방법에 관해 이야기 한다.


Testing on the UI thread

  • 어플의 Activity는 어플리케이션 UI 스레드상에서 동작한다. 일단 UI가 onCreate() 메소드등을 통해 인스턴스화 되면, 해당 UI와 연관된 모든 상호작용은 UI 스레드 위에서 처리되어야 한다. 만일 일반적인 경우라면 이런 일에 관하여 신경쓸 필요가 없다.
  • 하지만 이 내용은 어플을 테스트하려고 한다면 변화가 생긴다. Instrumentation을 이용하면, 여러분은 어플리케이션의 특정 UI 요소에 관한 테스트 메서드를 수행할수 있다. 두가지 방법이 있다. 테스트 메서드 전체가 UI 스레드에서 동작 시키도록 @UIThreadTest 어노테이션을 사용할 수 있다. 이 경우, 모든 테스트 메서드가 UI 스레드에서 동작하기 때문에, Instrumentation.waitForIdleSync()와 같이 UI와 상호작용하지 않는 메서드들은 사용할 수 없다.
  • 테스트 메서드 중 일부분을 UI 스레드 상에서 동작시키고 싶을 경우에는, 익명의 Runnable 클래스를 사용하면 된다. 수행하고자 하는 구문을 run() 메서드 안에 추가한 후, 새로운 인스턴스를 하나 생성해, Activity.runOnUiThread() 메서드를 이용하여 해당 구문을 UI 스레드 위에서 동작시킬 수 있다.
  • 예를 들어, 아래의 코드는 테스트 대상이 되는 Activity를 하나 생성한 후, 해당 Activity에 존재하는 Spinner가 포커스를 갖도록 메서드를 호출한다.(UI 액션). 그후에, 키 이벤트를 전달한다. 주의깊게 살펴보아야 할 점은 waitForIdleSync()와 sendKeys()메서드는 UI 스레드 상에서 호출될 수 없다는 점이다.
private MyActivity mActivity;
private Spinner mSpinner;

protected void setUp() throws Exception 
{
    super.setUp();
    mInstrumentation = getInstrumentation();

    mActivity = getActivity(); // get a references to the app under test
    mSpinner = (Spinner) mActivity.findViewById(com.android.demo.myactivity.R.id.Spinner01);
}

public void aTest() 
{
	mActivity.runOnUiThread(new Runnable() 
	{
        public void run() 
        {
            mSpinner.requestFocus();
        }
    });

    mInstrumentation.waitForIdleSync();

    this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
}


Turning Off Touch Mode

  • 키 이벤트를 통해 에뮬레이터나 디바이스를 컨트롤 하기 위해서는, 우선 터치 모드를 꺼야 한다. 만일 그렇지 않으면 키 이벤트는 무시된다.
  • 터치모드 Off : getActivity()를 호출하여서, Activity를 시작하기전에 Off를 하여주어야 한다.
  • EX) ActivityInstrumentationTestCase2.setActivityTouchMode(false); 를 호출하여준다. 또 이 메서드는 UI 스레드가 아닌 별도의 스레드 위에서 호출되어야한다. 때문에, 여러분은 이 메서드를 @UIThread 어노테이션이 사용된 메서드내에서는 사용할 수 없고, 이런 경우, setUp() 메서드 상에서 미리 호출해야 한다.

Unlocking the Emulator or Device
  • 에뮬레이터나 디바이스의 홈스크린이 잠겨있다면, 테스트 중인 어플리케이션이 키 이벤트를 수신할 수 없기 때문에 UI 테스트가 작동하지 않는다. 이 문제를 해결할 가장 좋은 방법은 에뮬레이터나 디바이스 상에서 키 가드 기능을 끄는 것이다.
  • 코드 상에서 명시적으로 화면이 잠기는것을 방지 할수도 있다. 우선 메니페스트 파일에 퍼미션이 추가되어야 하고, 테스트 대상이 되는 어플리케이션 상에서 키 가드 기능을 꺼야한다. 어플리케이션을 배포할 때는 이러한 내용응 다시 원래대로 수정해야 한다는 점을 명심하라.
  • 퍼미션을 추가하기 위해서는 메니페스트 파일에 다음 엘리먼트를 추가하라. <uses-permissionandroid:name="android.permission.DISABLE_KEYGUARD"/> 키 가드 기능을 실제로 정지 시키기 위해서는 onCreate() 상에 아래와 같은 코드를 적용하면 된다.
mKeyGuardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
mLock = mKeyGuardManager.newKeyguardLock("activity_classname");
mLock.disableKeyguard();


Troubleshooting UI Tests

  • UI테스팅을 수행할 때 일반적으로 발생하는 문제와 그 원인에 관하여 이야기한다.

WrongThreadException
  • 문제 : 다음과 같은 오류 메세지가 발생한다. android.view.ViewRoot $ CalledFromWrongThreadException : Only the original thread that created a view hierarchy can touch its views.
  • 추정 원인 : 이 문제는 일반적으로 UI 스레드가 아닌 스레드 위에서 UI 이벤트를 전달 할 때 발생한다. 테스트 어플리케이션 상에서 UI 이벤트를 전달하면서 @UIThread 어노테이션을 빼먹거나, runOnUiThread() 메서드를 사용하지 않은 경우에 발생한다.
  • 권장 해결 방안: 테스트 메서드를 UI 스레드 상에서 실행 시킨다. Testing on the UI Thread 항목을 참고하라.

java.lang.RuntimeException

  • 문제 : 테스트에 실피할 경우 다음과 같은 오류가 발생한다. java.lang.RuntimeException: This method can not be called from the main application thread
  • 추정 원인 : 이 오류는 일반적으로 @UiThreadTest 어노테이션이 추가된 메서드 내에서 혹은 runOnUithread() 메서드 내부에서, UI 스레드 외부에서 호출해야할 메서드를 사용하는 경우에 발생한다. 
  • 권장 해결 방안: @UiThreadTest 어노테이션이나 runOnUiThread() 메서드 호출을 없애라. 혹은 테스트 케이스를 리팩토링 하라.



'Mobile > Android' 카테고리의 다른 글

Hello, Testing  (0) 2014.03.07
Testing in Other IDEs  (0) 2014.03.07
Data backup  (0) 2014.02.19
[Android]화면 밝기 관련  (0) 2014.02.10
Testing. Eclipse with ADT  (0) 2014.02.07
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함