Mocking up WifiManager para la prueba de unidad Android

Estoy tratando de implementar algunas pruebas de unidad para un par de clases que dependen de WifiManager y los ScanResults devueltos. Lo que me gustaría hacer es poder controlar los ScanResults que estoy recibiendo para probar una variedad de condiciones diferentes.

Por desgracia ha sido bastante difícil para mí con éxito mock WifiManager (aunque supongo que puedo pasar su constructor referencias nulas en mi MockWifiManager). Esto sólo será mi primer problema, ya que una vez que tenga un MockWifiManager para jugar con (si esto funciona!) Voy a tener que crear con éxito mi prueba ScanResults que no tiene un constructor público (Imagínese que es creado por alguna fábrica en algún lugar).

Preguntas: ¿Con él no tener un constructor público puedo incluso extenderlo?

¿Estoy haciendo todo esto mal? A menudo me hacen preguntas acerca de cómo hacer una tarea específica, pero realmente están tratando de resolver un problema diferente de la manera equivocada, tal vez eso es lo que estoy haciendo aquí?

Soy muy nuevo en Android, por lo que tener que simular toda esta funcionalidad ha estado tratando de decir lo menos.

Gracias por sus entradas!

Edit: Estoy teniendo un infierno de una instancia de un MockWifiManager también. El constructor de wifi manager está esperando un IWifiManager un tipo que no parece existir en el SDK de Android.

Crea una abstracción alrededor de WifiManager. Utilice esto para su burla. Las cosas burlonas que no posees son duras y quebradizas. Si se hace bien, debería ser capaz de cambiar los internos, además de que terminará con una mejor API de imitación.

Para su prueba usted puede stub / falsificar el encargado a su contenido de los corazones. Para la producción pasarás en una instancia concreta.

Con respecto a su punto acerca de cambiar su código sólo para que sea comprobable que es incorrecto. En primer lugar usted debe burlarse de papeles no tipos como se discute en el papel a continuación. Google para más información.

En segundo lugar, la creación de una abstracción en torno a código de terceros es una buena práctica como lo indica el principio de inversión de dependencia en SOLID. Siempre debe depender de las abstracciones en lugar de las implementaciones concretas si usted es la unidad de prueba o no.

http://www.objectmentor.com/resources/articles/dip.pdf http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod

Puede intentar crear las instancias de ScanResult mediante la reflexión para acceder a los constructores privados. El código podría ser algo como esto:

try { Constructor<ScanResult> ctor = ScanResult.class.getDeclaredConstructor(null); ctor.setAccessible(true); ScanResult sr = ctor.newInstance(null); sr.BSSID = "foo"; sr.SSID = "bar"; // etc... } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } 

Para otras formas de prueba, la mayoría de las veces convertir la información de instancias como ScanResult y encapsular sólo la información que necesito en mis propios objetos. Estos alimentan al método haciendo el trabajo duro. Esto facilita la realización de pruebas, ya que se pueden construir fácilmente estos objetos intermedios sin depender de los objetos ScanResult reales.

He estado luchando por un tiempo para construir el objeto ScanResult . He utilizado con éxito ese enfoque de reflexión anterior.

Si alguien está buscando una forma de clonar el objeto ScanResult (o cualquier otro objeto que implemente la interfaz Parcelable ) puede usar este enfoque (lo revisé en una prueba de unidad):

 @RunWith(RobolectricTestRunner.class) @Config(manifest=Config.NONE) public class MovingAverageQueueTests { @Test public void parcelTest() { Parcel parcel = Parcel.obtain(); ScanResult sr = buildScanResult("01:02:03:04:05:06", 70); parcel.writeValue(sr); parcel.setDataPosition(0); // required after unmarshalling ScanResult clone = (ScanResult)parcel.readValue(ScanResult.class.getClassLoader()); parcel.recycle(); assertThat(clone.BSSID, is(equalTo(sr.BSSID))); assertThat(clone.level, is(equalTo(sr.level))); assertThat(clone, is(not(sameInstance(sr)))); } private ScanResult buildScanResult(String mac, int level) { Constructor<ScanResult> ctor = null; ScanResult sr = null; try { ctor = ScanResult.class.getDeclaredConstructor(null); ctor.setAccessible(true); sr = ctor.newInstance(null); sr.BSSID = mac; sr.level = level; } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return sr; } } 

Y en cuanto al rendimiento, este cheque ingenuo:

 @Test public void buildVsClonePerformanceTest() { ScanResult sr = null; long start = System.nanoTime(); for (int i = 0; i < 1000000; i++) { sr = buildScanResult("01:02:03:04:05:06", 70); } long elapsedNanos = System.nanoTime() - start; LOGGER.info("buildScanResult: " + elapsedNanos); start = System.nanoTime(); for (int i = 0; i < 1000000; i++) { sr = cloneScanResult(sr); } elapsedNanos = System.nanoTime() - start; LOGGER.info("cloneScanResult: " + elapsedNanos); } 

Mostró estos resultados:

Oct 26, 2016 3:25:19 PM com.example.neutrino.maze.MovingAverageQueueTests buildVsClonePerformanceTest INFO: buildScanResult: 202072179 26 de octubre 2016 3:25:21 PM com.example.neutrino.maze.MovingAverageQueueTests buildVsClonePerformanceTest INFO: cloneScanResult: 2004391903

Así que la clonación de esta manera es 10 veces menos eficaz que la creación de instancia, incluso con la reflexión. Sé que esta prueba no es robusta como las optimizaciones se realizan durante la compilación … Sin embargo factor de diez es difícil de mitigar. También hice 10K iteraciones probadas y entonces el factor era incluso 100! Solo para tu información.

PS obtener Parcel.obtain () y parcel.recycle fuera de bucle no ayuda

  • ¿Qué tipo de aplicaciones de Android son las más difíciles de soportar (preocupación de fragmentación)
  • Pruebas de Android sin emulador
  • Compartir código entre pruebas de instrumentación de Android y pruebas de unidad en Android Studio
  • Android: Espresso no espera hasta que se muestre fragmento o actividad para que cada prueba fallara
  • Android: Escribir casos de prueba para Fragmentos
  • Probando el proyecto de Android con jar cifras
  • Gradle: Cómo ejecutar la prueba de instrumentación para la clase
  • Cómo ejecutar proyecto de prueba de unidad de android
  • Haga que Espresso espere a que WebView termine de cargar
  • Java.lang.ClassNotFoundException: android.R
  • Cómo obtener el texto visible de la contraseña EditText (después de PasswordTransformation - puntos)?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.