2012-04-27 19 views
7

W jaki sposób mogę utworzyć przypadek testowy JUnit Androida, który testuje treść intencji wygenerowanej w ramach działania?W jaki sposób mogę przetestować intencję uruchomioną/wysłaną z działania?

Mam działanie, które zawiera okno EditText, a po zakończeniu wprowadzania wymaganych danych, Aktywność uruchamia Intent do IntentService, która rejestruje dane i kontynuuje proces aplikacji. Tutaj jest klasa chcę przetestować The OnEditorActionListener/PasscodeEditorListener jest tworzony jako osobnej klasy:

public class PasscodeActivity extends BaseActivity { 
    EditText     m_textEntry = null; 
    PasscodeEditorListener  m_passcodeEditorListener = null; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.passcode_activity); 

     m_passcodeEditorListener = new PasscodeEditorListener(); 
     m_textEntry = (EditText) findViewById(R.id.passcode_activity_edit_text); 
     m_textEntry.setTag(this); 
     m_textEntry.setOnEditorActionListener(m_passcodeEditorListener); 
    } 

    @Override 
    protected void onPause() { 
     super.onPause(); 
     /* 
     * If we're covered for any reason during the passcode entry, 
     * exit the activity AND the application... 
     */ 
     Intent finishApp = new Intent(this, CoreService.class); 
     finishApp.setAction(AppConstants.INTENT_ACTION_ACTIVITY_REQUESTS_SERVICE_STOP); 
     startService(finishApp); 
     finish(); 
    } 

} 



class PasscodeEditorListener implements OnEditorActionListener{ 
    @Override 
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 
     PasscodeActivity activity = (PasscodeActivity) v.getTag(); 
     boolean imeSaysGo = ((actionId & EditorInfo.IME_ACTION_DONE)!=0)?true:false; 
     boolean keycodeSaysGo = ((null != event) && 
       (KeyEvent.ACTION_DOWN == event.getAction()) && 
       (event.getKeyCode() == KeyEvent.KEYCODE_ENTER))?true:false; 

     if (imeSaysGo || keycodeSaysGo){ 
      CharSequence seq = v.getText(); 
      Intent guidEntry = new Intent(activity, CoreService.class); 
      guidEntry.setAction(AppConstants.INTENT_ACTION_PASSCODE_INPUT); 
      guidEntry.putExtra(AppConstants.EXTRA_KEY_GUID, seq.toString()); 
      activity.startService(guidEntry); 
      return true; 
     } 
     return false; 
    } 
} 

Jak mogę przechwycić dwa możliwe intencjami wychodzące generowane przez działalność i sprawdzić ich zawartość?

Dzięki

+0

Czy używasz symulatora? Może czegoś mi brakuje, ale czy nie możesz tego tak przetestować? – Nick

+0

Używam zarówno symulatora, jak i słuchawki, chociaż nie sądzę, że powinna istnieć jakakolwiek różnica. Widziałem wiele sposobów na wstrzyknięcie Intents do jakiejkolwiek konkretnej aktywności w trakcie testu, ale nie wiele sposobów oglądania wyników. Widziałem inny post, gdy ustawili ContextWrapper i przechwycili wywołanie funkcji "startService()". Działa to dla pierwszego połączenia, ale nie dla kolejnych połączeń. Działanie może uruchomić wiele intencji bez zamykania, jestem zainteresowany oglądaniem/testowaniem ich wszystkich. –

Odpowiedz

6

Pomyślałem, jak korzystać ContextWrapper z pomocą innej stronie.

Użyj ContextWrapper i zastąp wszystkie zamierzone funkcje. Generalizując dla wszystkich moich testów aktywności, rozszerzyłem klasę ActivityUnitTestCase i zaimplementowałem rozwiązanie jako podkładkę. Enjoy:

import android.app.Activity; 
import android.app.Instrumentation; 
import android.content.ComponentName; 
import android.content.Context; 
import android.content.ContextWrapper; 
import android.content.Intent; 
import android.test.ActivityUnitTestCase; 

public class IntentCatchingActivityUnitTestCase<T extends Activity> extends ActivityUnitTestCase<T> { 

    protected Activity m_activity; 
    protected Instrumentation m_inst; 
    protected Intent[] m_caughtIntents; 
    protected IntentCatchingContext m_contextWrapper; 

    protected class IntentCatchingContext extends ContextWrapper { 
     public IntentCatchingContext(Context base) { 
      super(base); 
     } 

     @Override 
     public ComponentName startService(Intent service) { 
      m_caughtIntents = new Intent[] { service }; 
      return service.getComponent(); 
     } 

     @Override 
     public void startActivities(Intent[] intents) { 
      m_caughtIntents = intents; 
      super.startActivities(intents); 
     } 

     @Override 
     public void startActivity(Intent intent) { 
      m_caughtIntents = new Intent[] { intent }; 
      super.startActivity(intent); 
     } 

     @Override 
     public boolean stopService(Intent intent) { 
      m_caughtIntents = new Intent[] { intent }; 
      return super.stopService(intent); 
     } 
    } 

    // --// 
    public IntentCatchingActivityUnitTestCase(Class<T> activityClass) { 
     super(activityClass); 
    } 

    protected void setUp() throws Exception { 
     super.setUp(); 
     m_contextWrapper = new IntentCatchingContext(getInstrumentation().getTargetContext()); 
     setActivityContext(m_contextWrapper); 
     startActivity(new Intent(), null, null); 
     m_inst = getInstrumentation(); 
     m_activity = getActivity(); 
    } 

    protected void tearDown() throws Exception { 
     super.tearDown(); 
    } 

} 
+0

To jest dobre rozwiązanie, niestety działa tylko z 'ActivityUnitTestCase', ale nie z przypadkami testów funkcjonalnych. –

+0

Tak, zgadzam się. Odkryłem także, że nie jest możliwe złapanie kilku intencji, na przykład moja Aktywność może wysłać Intencję na niektóre warunki uruchamiania do IntentService, a następnie uruchomić inną, gdy użytkownik naciśnie przycisk. Nie można złapać wszystkich. –

1

Alternatywnie, można re-factor swój kod w celu zrobienia „czystej” Unit-test (mam na myśli badanej jednostki, która ma wszystko, szydzili z wyjątkiem klasy badanego). Właściwie to sam mam sytuację, w której dostaję java.lang.RuntimeException: Stub!, ponieważ kod, który chcę przetestować w jednostce, tworzy nowe intencje zawierające mocks, które wstrzyknąłem.

Rozważam stworzenie własnej fabryki do zamiarów. Wtedy mogę wstrzyknąć szydzili z fabryki do mojego testu klasa-under-:

public class MyClassToBeTested { 
    public MyClassToBeTested(IntentFactory intentFactory) { 
     //assign intentFactory to field 
    } 
    .... 
    public void myMethodToTestUsingIntents() { 
     Intent i = intentFactory.create(); 
     i.setAction(AppConstants.INTENT_ACTION_PASSCODE_INPUT); 
     //when doing unit test, inject a mocked version of the 
     //IntentFactory and do the necessary verification afterwards. 
     .... 
    } 
} 

Moja sytuacja nie jest takie same, ale wierzę, że można zastosować fabrycznie wzorzec go rozwiązać, jak również. Wolę pisać kod, aby wspierać prawdziwe testy jednostkowe, ale muszę przyznać, że twoje rozwiązanie jest dość sprytne.

Powiązane problemy