2011-02-11 6 views
17

여기에 뭔가 빠져 있어야합니다. ActivityUnitTestCase의 JavaDoc은이 테스트 사례가 시스템과 분리 된 활동을 테스트 할 것을 제안합니다.ActivityUnitTestCase가 Application.onCreate를 호출하지 못하게하는 방법은 무엇입니까?

이 클래스는 단일 활동에 대한 독립적 인 테스트를 제공합니다. 테스트중인 액티비티는 시스템 인프라에 최소한의 연결만으로 만들어지며 많은 액티비티 종속성의 조롱 된 버전이나 래퍼 버전을 삽입 할 수 있습니다.

실제로 응용 프로그램을 시작하지 않는 것으로 가정하고있었습니다. 또한 setApplication 도우미를 노출하여 모의 응용 프로그램을 삽입하는 데 아마도 사용할 수 있습니다.

그러나 어떤 ActivityUnitTestCase 나 (실제) 응용 프로그램을 시작하고 해당 onCreate 메서드를 호출합니다. 정확하게 말하자면, InstrumentationTestRunner은 그 일을하고있는 것처럼 보입니다. 그렇기 때문에, 전에 setApplicationsetUp 메쏘드에 들어갈 기회가 생겼습니다! 테스트 스위트를 실행하는 동안 심지어 Eclipse 중단 점에 도달하지 못했지만 실제로 로그에 기록하면 onCreate이 실제로 호출되었다는 것을 알게되어 꽤 오래 동안 알 수 없었습니다.

이것은 완전히 저쪽에 있습니다. Android 테스트 주자가 실제 애플리케이션을 인스턴스화하고 실행하는 중에 mock 앱 객체를 사용하려는 이유는 무엇입니까? 이것은 계측 러너가 자체 스레드에서 실행되고이를 수행 할 때 주 응용 프로그램 스레드를 생성한다는 점을 감안할 때 더욱 문제가됩니다. 즉, 실행중인 테스트와 Application.onCreate이 호출되는 사이에 경쟁 조건이 있음을 의미합니다. 테스트 결과에 영향을 미칠 수있는 내용이 있다면 공유 환경 설정 파일에 쓰면 테스트가 무작위로 실패하기 때문에 완전히 망가져 버립니다.

테스트 프레임 워크에서 무언가가 누락되었거나 간단하게 표시 되었습니까?

업데이트 이것은 ApplicationTestCase에도 영향을 미치는 것으로 보입니다. 테스트 케이스가 시작되기 전에 어플리케이션 클래스 'onCreate에서 중단 점에 도달 할 수 있습니다. 우리는 무의식적으로 AsyncTask를 시작합니다. 무의식적으로 실패 할 것이기 때문에 무작위로 실패 할 것입니다. 기억하십시오. 내 테스트 케이스에서는 setUp이 호출되기 전에 있습니다. 저는 여기에서 onCreate이 모호한 호출 중에 참조 스택 추적은 다음과 같습니다

Thread [<1> main] (Suspended (breakpoint at line 86 in QypeRadar)) 
QypeRadar.onCreate() line: 86 
InstrumentationTestRunner(Instrumentation).callApplicationOnCreate(Application) line: 969 
ActivityThread.handleBindApplication(ActivityThread$AppBindData) line: 4244 
ActivityThread.access$3000(ActivityThread, ActivityThread$AppBindData) line: 125  
ActivityThread$H.handleMessage(Message) line: 2071 
ActivityThread$H(Handler).dispatchMessage(Message) line: 99 
Looper.loop() line: 123 
ActivityThread.main(String[]) line: 4627  
Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method] 
Method.invoke(Object, Object...) line: 521 
ZygoteInit$MethodAndArgsCaller.run() line: 868 
ZygoteInit.main(String[]) line: 626 
NativeStart.main(String[]) line: not available [native method] 

이유는 무엇입니까 테스트 러너 callApplicationOnCreatethe docs하지만 분명히 상태 :

테스트 케이스에서 onCreate()를 할 때까지 호출 할 것이다 테스트는 createApplication()을 호출합니다. 따라서 onCreate() 전에 추가 프레임 워크 또는 테스트 논리를 설정하거나 조정할 수 있습니다.

평평한 거짓말 - 내게 기회가되지 않습니다!

+0

내가 알 수있는 한,이 문제를 해결할 수있는 방법은 두 가지뿐입니다. 1) InstrumentationTestRunner를 다시 작성하여 callApplicationOnCreate를 호출하지 않고 테스트 케이스에 남겨 두어 원하는지 여부를 결정합니다. 하지만 다른 테스트에서 어떤 영향을 미칠지 확실하지 않습니다. 2) 우리의 onCreate 메소드를 부작용이없고 멱등 한 것으로 다시 작성하십시오. (테스트 케이스가 작동하기 때문에'createApplication'도 호출해야합니다.) 생각? – Matthias

+0

그래서 callApplicationOnCreate를 우회하는 커스텀 테스트 러너를 만들었습니다. 이제는 모든 것이 * unit * 테스트에서 예상대로 작동합니다. 기능 테스트의 경우 전체 실행을 수행하는 기본 테스트 러너가 필요하므로 이제 테스트 용 러너가 2 개 있습니다. 이는 약간의 고통입니다. – Matthias

+0

흥미로운 글쓰기. 적절한 테스트 케이스 유형에 대해서만'callApplicationOnCreate'를 우회하는 것만으로도 충분히 간단 할 수 있습니다. 여러분은 업스트림으로 보낼 수있는 테스트 러너 용 패치를 만들 수 있습니까? –

답변

3

Roboguice 같은 문제가있었습니다. 확인하십시오 here.

+3

+1 유용한 자료는 이전에 'attachBaseContext'에 대해 알지 못했습니다! 그러나 도움이되지 않습니다 : 테스트 실행자는 여전히 인수가없는 생성자를 호출하고 제거하면 예외가 발생합니다. "InstantiationException - newInstance failed : no ()". no-args ctor를 그대로두면 setApplication을 호출하는지 여부에 관계없이 onCreate가 원래 app 객체에서 계속 호출되므로 문제가 남아 있습니다. – Matthias

+0

이것은 이론적으로 질문에 대답 할 수 있지만 여기에 대답의 핵심 부분을 포함하고 참조 용 링크를 제공하는 것이 바람직합니다 (// meta.stackoverflow.com/q/8259). – nhaarman

2

대거로 테스트를하고 있습니다. 아마도 애플리케이션에 무엇이든지 삽입하고 호출하지 않으려 고하기 때문에 아마도 이것도 마찬가지입니다.에서 onCreate, 그래서이 사람이 나를 (api17의 +)에 대한 작동합니다 : 당신이 그렇지 않으면 충돌 것, 반사를 사용하고 Application.mLoadedApk을 설정하는 대신 Application.attachBaseContext()Application.attach(context)를 호출 할 필요가

private Context mContext; 
private Application mApplication; 

@Override 
protected void setUp() throws Exception { 
    super.setUp(); 

    mContext = new ContextWrapper(getInstrumentation().getTargetContext()) { 
     @Override 
     public Context getApplicationContext() { 
      return mApplication; 
     } 
    }; 
    mApplication = new MyAppMock(); 
    mApplication.attachBaseContext(mContext); 

    setApplication(app); 
} 

public void testActivityCreated() { 
    Intent intent = AboutActivity.createIntent(mContext); 
    setActivityContext(mContext); 
    startActivity(intent, null, null); 
    assertNotNull(getActivity()); 
} 

api16 <하십시오.

내가 모든 것을 함께 넣고 단검으로 테스트하는 방법을 보여줍니다 데모 응용 프로그램 만들었 : https://github.com/vovkab/dagger-unit-test

또한 응용 프로그램을 조롱하는 방법을 보여줍니다 모든 안드로이드 버전을 작동합니다.

+0

"단검 - 유닛 테스트"테스트 클래스를 테스트 해 보셨습니까? 나는이 방법을 직접 시도했지만 Dagger가 TestModule에 등록 된 대상 클래스가 없다고 불평하기 때문에 실패했습니다. 대상 클래스를 익명 클래스 MyTestClass $ 2 대신 InjectableAppMock (이는 모듈의 "inject"속성에 지정된 것입니다. "Module"annotation – twelve17

+0

언급 한 클래스 이름이 일치하지 않거나이 예제 나 예제가 github에서 호스트되는 예제 코드에 대해 잘 모르겠습니다. github에서 호스팅되는 샘플 응용 프로그램에서 MainActivity 클래스를 삽입합니다. @Module (injects = MainActivity.class). 예, 잘 작동합니다. 예를 들어 여기를보세요. https://github.com/vovkab/dagger-unit-test – vovkab

+0

더 빨리 응답하지 않는 것에 대해 사과드립니다. 잘못된 코드 - 방금 작업 한 프로젝트로 돌아 왔고, 내가 깨닫게 된 것은 ['setActivityContext (mContext);'] (https://github.com/vovkab/)에 대한 호출을 놓쳤다는 것입니다. dagger-unit-test/blob/master/app/s rc/androidTest/java/vovkab/daggerexample/MainActivityUnitTest.java # L59)를 작성하십시오. 감사! – twelve17

관련 문제