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).
- Cómo utilizar la secuencia de comandos de Perl para la prueba de la aplicación android utilizando eclipse épica?
- Obtener incompatible AVD error al ejecutar la prueba de instrumentación de iones Gitlab CI
- ¿Cómo puedo probar un clic en una determinada coordenada x, y?
- ¿Cómo hacer pruebas escalares con Android?
- No deje de generar gradle si una prueba falla con el gradle-android-test-plugin
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.
- Android UIAutomator haga clic en el dispositivo
- Jacoco con Gradle 0.10.0: Objeto remoto no existe
- Prueba de varios dispositivos Android en una máquina
- Descenso de la versión androide de Phonegap de 3.40 a 2.9.0
- Cómo corregir la excepción de permiso INJECT_EVENT al enviar toques a una prueba de ActivityInstrumentationTestCase2
- VerifyError en las herramientas de compilación del proyecto de prueba android v17
- ¿Cómo aceptar el diálogo en las pruebas unitarias?
- Cómo probar con Espresso un PendingIntent generado por TaskStackBuilder
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