Как я могу тестировать Android-активность, действующую на Accelerometer?

Я начинаю с Activity, основанного на этом ShakeActivity, и я хочу написать для него некоторые модульные тесты. Я написал несколько небольших тестов для Android, но не знаю, с чего начать. Я хочу передать акселерометру несколько разных значений и проверить, как реагирует на него активность. На данный момент я сохраняю это просто и просто обновляю переменную private int counter и TextView, когда происходит событие «встряхивания».

Поэтому мой вопрос во многом сводится к следующему:

Как я могу отправить поддельные данные на акселерометр из модульного теста?

Solutions Collecting From Web of "Как я могу тестировать Android-активность, действующую на Accelerometer?"

Мое решение этого оказалось проще, чем я ожидал. Я не тестирую акселерометр, так как тестирую реакцию приложения на событие, возбужденное акселерометром, и мне просто нужно было проверить его. Мой класс реализует SensorListener, и я хотел проверить, что происходит onSensorChanged. Ключ тогда должен был подпитывать некоторые значения и проверять состояние моей активности. Пример:

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

Как я могу отправить поддельные данные на акселерометр из модульного теста?

AFAIK, вы не можете.

Попросите вашу шейкерную логику использовать подключаемый источник данных. В модульном тесте поставьте макет. В производстве поставьте обертку вокруг акселерометра.

Или, не волнуйтесь о модульном тестировании самого шейкера, а скорее беспокоитесь об аппаратном тестировании вещей, которые используют шейкер, и создайте макет шейкера.

Ну, вы можете написать интерфейс.

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

Напишите FakeAccelerometerReader и FakeAccelerometerReader . Ваш код будет использовать IAccelerometerReader но вы можете поменять его на Android или Fake.

Не нужно тестировать акселерометр ОС, просто проверьте свою собственную логику, которая отвечает на ОС – другими словами, ваш SensorListener . К сожалению, SensorEvent является закрытым, и я не мог SensorListener.onSensorChanged(SensorEvent event) вызвать SensorListener.onSensorChanged(SensorEvent event) , поэтому должен был сначала подклассифицировать SensorListener своим собственным классом и вызвать собственный метод непосредственно из тестов:

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

Затем я могу вызвать onSensorUpdated непосредственно из моего тестового кода, который имитирует акселерометр.

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

Вы можете проверить выше часть кода следующим образом

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