2010-05-10 17 views
5

Estoy empezando con una actividad basada en este ShakeActivity y quiero escribir algunas pruebas de unidad para ello. He escrito algunas pruebas de unidades pequeñas para actividades de Android anteriormente, pero no estoy seguro de por dónde empezar aquí. Quiero alimentar el acelerómetro con diferentes valores y probar cómo responde la actividad. Por ahora lo estoy manteniendo simple y simplemente actualizando una variable privada de contador interno y un TextView cuando ocurre un evento de "sacudida".¿Cómo puedo probar una unidad de una actividad de Android que actúa sobre el acelerómetro?

Así que mi pregunta se reduce en gran medida reduce a esto:

¿Cómo puedo enviar datos falsos para el acelerómetro de una prueba de unidad?

Respuesta

5

Mi solución a esto terminó siendo mucho más simple de lo que esperaba. Realmente no estoy probando el acelerómetro tanto como estoy probando la respuesta de la aplicación a un evento provocado por el acelerómetro, y solo necesitaba probar en consecuencia. Mi clase implementa SensorListener y quería probar qué sucede enSensorChanged. La clave era alimentar algunos valores y verificar el estado de mi Actividad. Ejemplo:

public void testShake() throws InterruptedException { 
    mShaker.onSensorChanged(SensorManager.SENSOR_ACCELEROMETER, new float[] {0, 0, 0}); 
    //Required because method only allows one shake per 100ms 
    Thread.sleep(500); 
    mShaker.onSensorChanged(SensorManager.SENSOR_ACCELEROMETER, new float[] {300, 300, 300}); 
    Assert.assertTrue("Counter: " + mShaker.shakeCounter, mShaker.shakeCounter > 0); 
} 
4

¿Cómo puedo enviar datos falsos a la acelerómetro de una prueba de unidad?

AFAIK, no puede.

Haga que su lógica de agitador acepte una fuente de datos conectable. En la prueba unitaria, suministre un simulacro. En producción, suministre una envoltura alrededor del acelerómetro.

O bien, no se preocupe por la unidad de prueba del agitador, sino más bien por la unidad de prueba de elementos que usan el agitador, y cree un simulador de agitación.

+0

Esto ciertamente tiene sentido y suena como una idea mejor. ¿Tiene algún ejemplo de utilizar una "fuente de datos conectables" como esta? –

+0

¿Esta respuesta todavía está actualizada? ¿O ha salido algo nuevo? – TinyTimZamboni

+1

@TinyTimZamboni: parece recordar que el equipo de herramientas de Android estaba trabajando en una forma de utilizar un dispositivo Android como entrada de sensor para un emulador, pero no sé dónde está eso.De lo contrario, no tengo conocimiento de ninguna forma de proporcionar una entrada de sensor falsa. – CommonsWare

1

Bueno, puede escribir una interfaz.

interface IAccelerometerReader { 
    public float[] readAccelerometer(); 
} 

La escritura de un AndroidAccelerometerReader y FakeAccelerometerReader. Su código usaría IAccelerometerReader pero puede intercambiar los lectores de Android o Fake.

0

No hay necesidad de probar el acelerómetro del sistema operativo, simplemente probar su propia lógica que responde al sistema operativo - en otras palabras, su SensorListener. Desafortunadamente SensorEvent es privada y no podía llamar SensorListener.onSensorChanged(SensorEvent event) directamente, por lo que tuvo que primero SensorListener subclase con mi propia clase, y llamar a mi propio método directamente de las pruebas:

public class ShakeDetector implements SensorEventListener { 

    @Override 
    public void onSensorChanged(SensorEvent event) { 

     float x = event.values[0]; 
     float y = event.values[1]; 
     float z = event.values[2]; 

     onSensorUpdate(x, y, z); 
    } 

    public void onSensorUpdate(float x, float y, float z) { 
     // do my (testable) logic here 
    } 
} 

Entonces puedo llamar onSensorUpdated directamente de mi código de prueba , que simula el disparo del acelerómetro.

private void simulateShake(final float amplitude, int interval, int duration) throws InterruptedException { 
    final SignInFragment.ShakeDetector shaker = getFragment().getShakeSensorForTesting(); 
    long start = System.currentTimeMillis(); 

    do { 
     getInstrumentation().runOnMainSync(new Runnable() { 
      @Override 
      public void run() { 
       shaker.onSensorUpdate(amplitude, amplitude, amplitude); 
      } 
     }); 
     Thread.sleep(interval); 
    } while (System.currentTimeMillis() - start < duration); 
} 
0
public class SensorService implements SensorEventListener { 
/** 
    * Accelerometer values 
    */ 
    private float accValues[] = new float[3]; 
    @Override 
    public void onSensorChanged(SensorEvent event) { 

      if (sensorEvent.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { 
      accValues[0] = sensorEvent.values[0]; 
      accValues[1] = sensorEvent.values[1]; 
      accValues[2] = sensorEvent.values[2]; 
     } 

    } 
} 

puede probar por encima de pieza de código siguiendo manera

@Test 
    public void testOnSensorChangedForAcceleratorMeter() throws Exception { 
     Intent intent=new Intent(); 
     sensorService.onStartCommand(intent,-1,-1); 

     SensorEvent sensorEvent=getEvent(); 
     Sensor sensor=getSensor(Sensor.TYPE_ACCELEROMETER); 
     sensorEvent.sensor=sensor; 
     sensorEvent.values[0]=1.2345f; 
     sensorEvent.values[1]=2.45f; 
     sensorEvent.values[2]=1.6998f; 
     sensorService.onSensorChanged(sensorEvent); 

     Field field=sensorService.getClass().getDeclaredField("accValues"); 
     field.setAccessible(true); 
     float[] result= (float[]) field.get(sensorService); 
     Assert.assertEquals(sensorEvent.values.length,result.length); 
     Assert.assertEquals(sensorEvent.values[0],result[0],0.0f); 
     Assert.assertEquals(sensorEvent.values[1],result[1],0.0f); 
     Assert.assertEquals(sensorEvent.values[2],result[2],0.0f); 
    } 




private Sensor getSensor(int type) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException { 
      Constructor<Sensor> constructor = Sensor.class.getDeclaredConstructor(new Class[0]); 
      constructor.setAccessible(true); 
      Sensor sensor= constructor.newInstance(new Object[0]); 

      Field field=sensor.getClass().getDeclaredField("mType"); 
      field.setAccessible(true); 
      field.set(sensor,type); 
      return sensor; 
     } 



private SensorEvent getEvent() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { 
     Constructor<SensorEvent> constructor = SensorEvent.class.getDeclaredConstructor(int.class); 
     constructor.setAccessible(true); 
     return constructor.newInstance(new Object[]{3}); 
    } 
Cuestiones relacionadas