Android - 在测试我的应用程序时如何模拟缺少相机?

标签 android unit-testing mockito

我想检查我的应用程序在其运行的设备没有摄像头时是否显示错误消息。我尝试传入一个模拟上下文,但是当我尝试模拟 CameraManager 类时,mockito 给出了一个错误,因为它被声明为 final。 android肯定有一个简单的解决方案吗?这是我的尝试:

public class CreateNewIdentityActivityUnitTest extends ActivityUnitTestCase<CreateNewIdentityActivity> {
    public CreateNewIdentityActivityUnitTest() {
        super(CreateNewIdentityActivity.class);
    }

    public void testErrorMessageDisplayedWhenNoCamerasExist() throws Exception {
        // Create the mock cameramanager
        // THIS LINE FAILS BECAUSE CAMERAMANAGER IS FINAL
        CameraManager mockCameraManager = mock(CameraManager.class);
        String[] cameraIdList = {};
        when(mockCameraManager.getCameraIdList()).thenReturn(cameraIdList);

        // Create the mock context
        Context mockContext = mock(Context.class);
        when(mockContext.getSystemService(Context.CAMERA_SERVICE)).thenReturn(mockCameraManager);

        // Start the activity
        Intent intent = new Intent(mockContext, CreateNewIdentityActivity.class);
        Activity activity = startActivity(intent, null, null);

        // Verify that the error message was made visible
        TextView errorTextView = (TextView)activity.findViewById(R.id.ErrorTextView);
        assertNotNull(errorTextView);
        assertEquals(View.VISIBLE, errorTextView.getVisibility());
    }
}

最佳答案

不幸的是,您不能模拟 final 类。

几乎没有选项/技巧:

  • 尝试添加Robolectric库并用它编写测试 ShadowCamera
  • 将与 CameraManager 相关的逻辑移动到一个单独的类中,并将其注入(inject)到 Activity 中。然后在测试项目中,您可以覆盖此注入(inject)。
  • 非常相似的想法 - 创建一个接口(interface) OnCameraManagerInterface

    public interface OnCameraManagerInterface {
         String[] getListOfCameras() throws CameraAccessException;
    }
    

    然后在Activity中实现:

    public class CreateNewIdentityActivity extends AppCompatActivity
            implements OnCameraManagerInterface {
        .......
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        @Override
        public String[] getListOfCameras() throws CameraAccessException {
            return ((CameraManager)getSystemService(Context.CAMERA_SERVICE)).
                      getCameraIdList();
        }
    }
    

    在代码中,您检查相机是否存在 - 调用:if (getListOfCameras().length == 0) {}

    现在,添加新的 TestCreateNewIdentityActivity 来覆盖您的 CreateNewIdentityActivity:

     public class TestCreateNewIdentityActivity extends CreateNewIdentityActivity {
        @Override
        public String[] getListOfCameras() throws CameraAccessException {
            return new String[0];
        }
    }
    

    在 list 中:

    <activity android:name=".TestCreateNewIdentityActivity"
            android:theme="@style/AppTheme.NoActionBar"/>
    

    测试看起来像:

    public class CreateNewIdentityActivityUnitTest extends ActivityUnitTestCase<TestCreateNewIdentityActivity> {
        public CreateNewIdentityActivityUnitTest() {
            super(TestCreateNewIdentityActivity.class);
        }
    
        public void testErrorMessageDisplayedWhenNoCamerasExist() throws Exception {
            // Verify that the error message was made visible
            TextView errorTextView = (TextView)getActivity().findViewById(R.id.ErrorTextView);
            assertNotNull(errorTextView);
            assertEquals(View.VISIBLE, errorTextView.getVisibility());
        }
    }   
    

    我很确定,即使不将 TestActivity 添加到主要源代码中并显示(将其保留在 androidTest 中,虽然我没有看),它也是可行的

  • 不创建新 Activity 的混合变体:

        public class ActivityCameraManager {
    
        private boolean isTest = false;
        private CameraManager cameraManager;
    
        public ActivityCameraManager(CameraManager cameraManager) {
            this.cameraManager = cameraManager;
        }
    
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        public String[] getListOfCameras() throws CameraAccessException {
            if (isTest) {
                return new String[0];
            }
    
            return cameraManager.getCameraIdList();
        }
    
        public void setTestMode() {
            isTest = true;
        }
    }
    

    那么您的 Activity 将如下所示:

        public class MainActivity extends AppCompatActivity  {
    
        ActivityCameraManager activityCameraManager;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ...
            activityCameraManager = new ActivityCameraManager((CameraManager) getSystemService(Context.CAMERA_SERVICE));
        }
    
        public void setActivityCameraManagerForTest() {
            activityCameraManager.setTestMode();
        }
    }
    

    在测试中只需调用 getActivity().setActivityCameraManagerForTest();

希望对你有帮助

关于Android - 在测试我的应用程序时如何模拟缺少相机?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34546409/

相关文章:

java - 两个ArrayList 一个RecyclerView Adapter

android - 在 Jetpack Compose 中创建 "nested"菜单的更好或更简单的方法是什么?

android - List View Item 行点击 Caused by : java. lang.NumberFormatException

android - 调用startActivity()后不调用父Activity的onDestroy()?

java - Mockito 模拟服务类

scala - 在 Mockito 中验证按名称的参数

java - 无法使用 powermockito 模拟安全管理器

python-3.x - flask 测试 : preprocess_request(), dispatch_request()

python - 是否有用于 Python 单元测试的可视化工具?

node.js - 如何测试 API 端点是否存在可由多个代码路径触发的错误路径?