我开始使用基于此ShakeActivity的Activity,我想为它编写一些单元测试。我之前已经为Android活动编写过一些小的单元测试,但我不知道从哪里开始。我想为加速度计提供一些不同的值,并测试活动如何响应它。现在我保持简单,只是在发生“摇动”事件时更新私有的int计数器变量和TextView。
所以我的问题很大程度上归结为:
如何从单元测试中将假数据发送到加速度计?
答案 0 :(得分:5)
我的解决方案最终比我预期的更简单。我没有真正测试加速度计,因为我正在测试应用程序对加速度计引发的事件的响应,我只需要进行相应的测试。我的类实现了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);
}
答案 1 :(得分:4)
如何将假数据发送给 加速度计来自单元测试?
AFAIK,你不能。
让您的振动器逻辑接受可插拔数据源。在单元测试中,提供模拟。在生产中,在加速度计周围提供包装。
或者,不要担心单元测试振动器本身,而是担心单元测试使用振动器的东西,并创建一个模拟振动器。
答案 2 :(得分:1)
好吧,你可以写一个界面。
interface IAccelerometerReader {
public float[] readAccelerometer();
}
写一个AndroidAccelerometerReader
和FakeAccelerometerReader
。您的代码将使用IAccelerometerReader
但您可以交换Android或Fake阅读器。
答案 3 :(得分:0)
无需测试操作系统的加速度计,只需测试自己的响应操作系统的逻辑 - 换句话说就是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);
}
答案 4 :(得分: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];
}
}
}
您可以按照以下方式测试上面的代码
@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});
}