2012-04-27 5 views
7

Come posso creare un test case Android JUnit che testa il contenuto di un Intento generato all'interno di un'attività?Come posso testare un intent lanciato/inviato da un'attività?

Ho un'attività che contiene una finestra EditText e quando l'utente ha terminato di immettere i dati richiesti, l'attività avvia un Intent su un IntentService che registra i dati e continua con il processo dell'applicazione. Qui è la classe che voglio provare, l'OnEditorActionListener/PasscodeEditorListener viene creato come una classe a parte:

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; 
    } 
} 

Come posso intercettare i due possibili Intenti in uscita generati dalle attività e verificare il loro contenuto?

Grazie

+0

Stai usando il simulatore? Forse mi manca qualcosa, ma non puoi semplicemente testarlo in questo modo? – Nick

+0

Ho usato sia il simulatore che il telefono, anche se non credo che ci dovrebbe essere una differenza. Ho visto un certo numero di modi per iniettare Intents in ogni particolare attività sotto test, ma non in molti modi di guardare l'output. Ho visto un altro post che hanno impostato ContextWrapper e intercettato la chiamata a "startService()". Funziona per la prima chiamata, ma non per le chiamate successive. Un'attività può lanciare più intenti senza chiudere, sono interessato a vederli/testarli tutti. –

risposta

6

ho pensato come utilizzare ContextWrapper con l'aiuto di un altro sito.

Utilizzare ContextWrapper e sovrascrivere tutte le funzioni di intent. Generalizzando per tutti i miei test di attività, ho esteso la classe ActivityUnitTestCase e implementato la soluzione come shim. Godetevi:

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

Questa è una bella soluzione, purtroppo funziona solo con 'ActivityUnitTestCase', ma non con i casi di test funzionali. –

+0

Sì, sono d'accordo. Ho anche scoperto che non è possibile catturare più Intenti, ad esempio, la mia Attività può inviare un Intento su alcune condizioni di avvio a un IntentService, quindi lanciarne un altro quando l'utente preme un pulsante. Non è possibile catturare tutto. –

1

alternativa, è possibile ri-factor il codice al fine di fare unità-test "pulito" (intendo una prova di unità che ha tutto deriso fuori tranne la classe in prova). In realtà, ho una situazione io stesso, dove ottengo un java.lang.RuntimeException: Stub! perché il codice che voglio testare crea nuovi Intenti che contengono mock che ho iniettato.

Considero di creare la mia propria fabbrica per intenti. Poi ho potuto iniettare una fabbrica deriso fuori per la mia prova di classe-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. 
     .... 
    } 
} 

La mia situazione non è la stessa come la tua, ma credo che si potrebbe applicare una fabbrica-modello per risolvere esso pure. Preferisco scrivere codice per supportare i veri test unitari, ma devo ammettere che la tua soluzione è abbastanza intelligente.

Problemi correlati