簡體   English   中英

如何對作用於Accelerometer的Android Activity進行單元測試?

[英]How can I unit test an Android Activity that acts on Accelerometer?

我從一個基於ShakeActivity的Activity開始,我想為它編寫一些單元測試。 我之前已經為Android活動編寫過一些小的單元測試,但我不知道從哪里開始。 我想為加速度計提供一些不同的值,並測試活動如何響應它。 現在我保持簡單,只是在發生“搖動”事件時更新私有的int計數器變量和TextView。

所以我的問題很大程度上歸結為:

如何從單元測試中將假數據發送到加速度計?

我對此的解決方案最終比我預期的更簡單。 我沒有真正測試加速度計,因為我正在測試應用程序對加速度計引發的事件的響應,我只需要進行相應的測試。 我的類實現了SensorListener ,我想測試onSensorChanged會發生什么。 接下來的關鍵是輸入一些值並檢查我的Activity的狀態。 例:

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

寫一個AndroidAccelerometerReaderFakeAccelerometerReader 您的代碼將使用IAccelerometerReader但您可以交換Android或Fake閱讀器。

無需測試操作系統的加速度計,只需測試您自己的響應操作系統的邏輯 - 換句話說就是您的SensorListener 遺憾的是, SensorEvent是私有的,我無法直接調用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});
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM