Cómo cambiar el título de las actividades en attach ()

Quiero ejecutar una prueba de instrumentación parametrizada con diferentes configuraciones regionales para ejecutar la misma prueba con todos los idiomas admitidos.

El comportamiento observado es que la actividad tendrá el título localizado de la primera prueba también para cada ejecución siguiente. Así que no importa el idioma en el que esté el teléfono, el título se localizará correctamente para la primera prueba de prueba parametrizada y seguirá siendo el mismo para cada uno siguiente.

Mientras que la sobreescritura de locales funciona para cualquier recurso, sólo funcionará una vez para el título de las actividades si lo configura AndroidManifest.xml .

Las actividades parecen obtener su título conjunto una vez en attach , y lo que está llamando adjunto parece estar en caché el título en la localidad de la aplicación se lanzó por primera vez en.

 final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, ---> CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor) { attachBaseContext(context); 

Dado que los recursos siempre se localizan correctamente, una solución sería llamar a setTitle(R.string.title) o simplemente a getActionBar().setTitle(R.string.setTitle) , pero me gustaría no cambiar las actividades únicamente con fines de prueba .

Pregunta: ¿Cómo puedo cambiar el título de la actividad se lanza con después de la primera prueba? Como se mencionó anteriormente, esto parece obtener en caché y no actualizado correctamente, y matar a la aplicación para reiniciar, fallará la prueba de instrumentación.

Configuración de prueba

Todo el proyecto de prueba se puede encontrar aquí en GitHub ( Localization.java contiene las pruebas de unidad que actualmente fallan con el problema descrito aquí) y está usando una prueba de unidad parametrizada en conjunción con UIAutomator .

El objetivo es tomar un lote de capturas de pantalla sin saber demasiado sobre la aplicación en sí (UIAutomator), y la aplicación no tener que ser modificado para la prueba tampoco.

Cambiar la configuración regional:

Estoy cambiando con éxito el entorno antes de cada prueba, y mis textos se muestran correctamente haciendo lo siguiente, también tengo varias afirmaciones en el lugar asegurándose de que los recursos son, de hecho, el escenario adecuado.

 public LocalizationTest(Locale locale) { mLocale = locale; Configuration config = new Configuration(); Locale.setDefault(mLocale); config.setLocale(mLocale); Resources resources = InstrumentationRegistry.getTargetContext().getResources(); resources.updateConfiguration(config, resources.getDisplayMetrics()); resources.flushLayoutCache(); } 

Lo que no funciona:

Obviamente traté de establecer la configuración regional de la misma manera en el contexto de destino, el contexto de la aplicación y la actividad (que probablemente sería demasiado tarde de todos modos).

Veo que se llama a attach de Instrumentation , pero sólo la creación de una nueva aplicación y tratando de iniciar la actividad no se localiza el título tampoco.

 Intent intent = context.getPackageManager().getLaunchIntentForPackage(BuildConfig.APPLICATION_ID); context = InstrumentationRegistry.getInstrumentation().newApplication(App.class, InstrumentationRegistry.getTargetContext()); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); 

La cadena de título se almacena en caché dentro del gestor de paquetes ApplicationPackageManager en un sStringCache estático.

Aunque hay un método static void configurationChanged() que borra el caché, no parece que se invoca en los cambios manuales. De ahí el problema descrito con el título de actividad mal localizada después de la primera invocación.

La solución a esto es posible mediante la reflexión para cargar la clase e invocar el método uno mismo. Esto es un poco sucio ya que está accediendo a un método privado, pero funciona.

 // as before Configuration config = new Configuration(); Locale.setDefault(mLocale); config.setLocale(mLocale); Resources resources = context.getResources(); resources.updateConfiguration(config, resources.getDisplayMetrics()); // CLEAR the cache! Method method = getClass().getClassLoader() .loadClass("android.app.ApplicationPackageManager") .getDeclaredMethod("configurationChanged"); method.setAccessible(true); method.invoke(null); 

Como alternativa, puede utilizar métodos públicos en otra API no pública que también invocará el método anterior. Todavía sucio pero no invocando métodos privados.

Parece que puedes omitir resources.updateConfiguration(...); Utilizando este método aunque ya que también se encargará de eso.

 // Clear the cache. Object thread = getClass().getClassLoader() .loadClass("android.app.ActivityThread") .getMethod("currentActivityThread") .invoke(null); Method method = getClass().getClassLoader() .loadClass("android.app.ActivityThread") .getMethod("applyConfigurationToResources", Configuration.class); method.invoke(thread, config); 

Descubrimos que el título de la actividad se establece en onAttach() con el título proporcionado por el gestor de actividades. Por lo tanto, creo que necesita cambiar la configuración del sistema en su lugar.

Para ello, la prueba puede utilizar la reflexión sobre ActivityManagerNative para actualizar la configuración:

 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public Localization(Locale locale) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { Context context = InstrumentationRegistry.getTargetContext(); log(context.toString()); log(context.getApplicationContext().toString()); mLocale = locale; Class<?> amClass = Class.forName("android.app.ActivityManagerNative"); Method getDefaultMethod = amClass.getDeclaredMethod("getDefault"); Object iActivityManager = getDefaultMethod.invoke(null /* static method */); Method updateConfigurationMethod = amClass.getMethod("updateConfiguration", Configuration.class); Configuration configuration = new Configuration(context .getResources().getConfiguration()); configuration.locale = locale; updateConfigurationMethod.invoke(iActivityManager, configuration); } 

Para ello, dé permiso a su aplicación (este permiso está firmado con las claves de depuración, no es suficiente para agregarla en el AndroidManifest)

 adb shell pm grant at.bleeding182.testing.instrumentationtest android.permission.CHANGE_CONFIGURATION 

He probado esta solución y puedo confirmar que el locale ahora está correctamente cambiado, y la prueba pasa – makeScreenshot es bastante escamosa, pero eso es para otro día.

Tendrá que cambiar el flujo de trabajo de su fábrica de software:

  1. Actualizar la aplicación apk y test apk
  2. Dar android.permission.CHANGE_CONFIGURATION a la prueba apk
  3. Ejecutar las pruebas
  • UiAutomator falla en el dispositivo 4.1.2
  • Prueba de UiAutomator 2.0 desde la línea de comandos
  • No se puede iniciar uiautomatorviewer
  • Android UIAutomator haga clic en el dispositivo
  • Cómo obtener una instancia de android.app.UiAutomation
  • UiAutomator getLastTraversedText ()
  • Android recibe texto del navegador
  • ¿Cómo puedo obtener el padre de una vista usando uiautomator?
  • Pruebas de Android: UIAutomator vs Espresso
  • Cómo automatizar el patrón de desbloqueo en un teléfono real utilizando uiautomator?
  • UIautomator cómo obtener el hijo por índice o instancia
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.