简体   繁体   English

如何在Android上使用Robolectric测试特定的硬件功能?

[英]How to test specific hardware features with Robolectric on android?

I started writing a robolectric test around hardware specific features, like sensors and camera (front and back). 我开始围绕硬件特定功能编写一个robolectric测试,如传感器和摄像头(前后)。

Imagine this class: 想象一下这堂课:

class CheckHardware {

    private bolean hasCamera(Context context) {
        PackageManager pm = context.callingActivityContext
            .getPackageManager();
        // camera support
        Boolean frontCam = pm.hasSystemFeature("android.hardware.camera.front");
        Boolean rearCam = pm.hasSystemFeature(PackageManager.FEATURE_CAMERA);
        if (frontCam || rearCam) {
            return true;
        }
        return false;
    }
}

So i want to test different scenarios where there is a front camera and a rear camera, only a front camera or no camera at all. 所以我想测试不同的场景,那里有前置摄像头和后置摄像头,只有前置摄像头或根本没有摄像头。 In my app its a bit more complicated but hope this makes it easier to understand what i mean. 在我的应用程序中它有点复杂,但希望这使我更容易理解我的意思。

For now i did it like this, which feels a bit odd. 现在我这样做了,感觉有点奇怪。

RobolectricPackageManager pm = (RobolectricPackageManager) Robolectric.application.getPackageManager();
pm.setSystemFeature(PackageManager.FEATURE_CAMERA, true);

I thought about writing my own test runner, so for all expected hardware settings a specific runner like this 我想过编写自己的测试运行器,所以对于所有预期的硬件设置,这样的特定运行器

public class WithCameraTestRunner extends RobolectricTestRunner {   
  @Override
  public void setupApplicationstate(RobolectricConfig robolectricConfig) {
    super.setupApplicationState(robolectricConfig);
    ShadowApplication shadowApplication = shadowOf(Robolectric.application);
    shadowApplication.setPackageName(robolectricConfig.getPackageName());
    RobolectricPackageManager pm = new RobolectricPackageManager(Robolectric.application, robolectricConfig)
    pm.setSystemFeature(PackageManager.FEATURE_CAMERA, true);
    shadowApplication.setPackageManager(pm);
  }
}

Not quite happy with that either as i want to test different scenarios in the same test. 对此不太满意,因为我想在同一测试中测试不同的场景。

Any ideas? 有任何想法吗? Whats the best approach for this? 这是最好的方法吗?

This can be done with JUnit rules. 这可以使用JUnit规则完成。 I wrote this example while using JUnit 4.8.2. 我在使用JUnit 4.8.2时编写了这个例子。

Here is the test class: 这是测试类:

public class CameraTest {

    @Rule
    public CameraRule rule = new CameraRule();

    @Test
    @EnableCamera
    public void testWithCamera() {
        Assert.assertTrue(CameraHolder.CAMERA);
    }

    @Test
    public void testWithoutCamera() {
        Assert.assertFalse(CameraHolder.CAMERA);
    }

}

Here is the rule: 这是规则:

import org.junit.rules.MethodRule;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;

public class CameraRule implements MethodRule {

    @Override
    public Statement apply(Statement base, FrameworkMethod method, Object target) {
        boolean camera = false;
        if (method.getAnnotation(EnableCamera.class) != null) {
            camera = true;
        }

        return new CameraStatement(camera, base);
    }

}

I included the imports so you can see from where they originated. 我包括了进口,所以你可以从它们的起源位置看到。 Statement is an interface with only a function evaluate() . Statement是一个只有一个函数evaluate()的接口。

Here is the statement class: 这是语句类:

import org.junit.runners.model.Statement;

public class CameraStatement extends Statement {

    private boolean mEnabled;
    private Statement mStatement;

    public CameraStatement(boolean enabled, Statement statement) {
        mEnabled = enabled;
        mStatement = statement;
    }

    @Override
    public void evaluate() throws Throwable {
        CameraHolder.CAMERA = mEnabled;
        mStatement.evaluate();
    }

}

Instead of an enabled boolean you can easily pass in an enumeration or a Set of enumerations for which features you want enabled. 您可以轻松传入要启用的功能的枚举或枚举Set ,而不是启用的boolean Make sure to disable all features that are not explicitly enabled. 确保禁用未明确启用的所有功能。 In this example, if you don't explicitly set enabled to true it will be false . 在此示例中,如果未将enabled显式设置为true则它将为false

Here is the code for the annotation itself: 以下是注释本身的代码:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnableCamera {
}

This would allow you to use this to annotate the function with @EnableCamera in addition to @Test . 除了@Test之外,这将允许您使用它来使用@EnableCamera注释函数。 If you want to have multiple features on for the same test you can put multiple annotations on the function. 如果要为同一测试启用多个功能,可以在该功能上添加多个注释。

For the sake of completeness here is the implementation of CameraHolder (but you won't need this): 为了完整起见,这里是CameraHolder的实现(但你不需要这个):

public class CameraHolder {
    public static boolean CAMERA = false;
}

I tried with robolectric, but my version of robolectric didn't have the function you were using: 我尝试使用robolectric,但我的版本的robolectric没有你使用的功能:

RobolectricPackageManager pm = (RobolectricPackageManager) Robolectric.application.getPackageManager();
// My version of robolectric didn't have this function.
pm.setSystemFeature(PackageManager.FEATURE_CAMERA, true);

I would probably have HardwareInfo class with methods hasBackCamera() and hasFrontCamera() . 我可能有使用方法hasBackCamera()hasFrontCamera() HardwareInfo类。 This class will require four tests - for each case of camera presence. 这个课程需要四个测试 - 每个摄像机存在的情况。 And all of them require you manipulation with Robolectric . 所有这些都需要你使用Robolectric进行操作。

For class that is doing real job I will mock HardwareInfo and have kind of setup methods: 对于正在做实际工作的类,我将模拟HardwareInfo并具有一些设置方法:

HardwareInfo infoWithFromCameraAndBackCamera() {
    HardwareInfo info = mock( HardwareInfo.class );

    when( info.hasBackCamera() ).thenReturn( true );
    when( info.hasFrontCamera() ).thenReturn( true );

    return info;
}

You could automate it/write less code as answer above/below with JUnit rules. 您可以使用JUnit规则自动执行/编写更少的代码作为上/下的答案。

Maybe I will go further I'm not sure if it's suitable for your case. 也许我会走得更远,我不确定它是否适合你的情况。 I would get rid of HardwareInfo usage directly in classes. 我会直接在类中删除HardwareInfo用法。 Basically I would have strategies classes which will behave correctly in specific hardware cases (similar to How to Replace (and not just move) Conditional Logic with Strategy? ). 基本上我会有策略类,它们在特定的硬件情况下会表现正常(类似于如何替换(而不仅仅是移动)带策略的条件逻辑? )。 But again it depends on your situation. 但这又取决于你的情况。 I wouldn't go with strategies if it's only one/two places where these conditions are used. 如果仅使用这些条件的一两个地方,我就不会选择策略。

@Test
public void hasCamera(){
    MainActivity activity = Robolectric.buildActivity( MainActivity.class )
            .create()
            .resume()
            .get();

    PackageManager packageManager = activity.getApplication().getPackageManager();
    ShadowPackageManager shadowPackageManager = shadowOf(packageManager);

    shadowPackageManager.setSystemFeature(PackageManager.FEATURE_CAMERA, true);
    assert(CameraUtils.checkCameraHardware(activity));

    shadowPackageManager.setSystemFeature(PackageManager.FEATURE_CAMERA, false);
    assert(!CameraUtils.checkCameraHardware(activity));

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM