Espresso test the camera intent when imageuri is passed as extra

Viewed 2287

I need to stub the camera intent by creating a image file at the path provided in the intent extra. Espresso can only respond with activityresult. Where can i perform the operation to create the file at passed path from intent extra.

Code for launching camera

File destination = new File(Environment.getExternalStorageDirectory(), "app_name" + System.currentTimeMillis() + ".jpg");

imageUri = FileProvider.getUriForFile(getApplicationContext(), getApplicationContext().getPackageName() + ".fileprovider", destination); Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);

startActivityForResult(intent, AppConstants.REQUEST_CODE_CAMERA);

Code for stubbing intent in test

Instrumentation.ActivityResult result = new Instrumentation.ActivityResult(Activity.RESULT_OK, null); intending(hasAction(MediaStore.ACTION_IMAGE_CAPTURE)).respondWith(result);

3 Answers

Ismael answer is perfect. For those looking for solution in java, Here it is.

intending(hasAction(MediaStore.ACTION_IMAGE_CAPTURE)).respondWith(
            new Instrumentation.ActivityResult(Activity.RESULT_OK, null));

IntentCallback intentCallback = new IntentCallback() {
        @Override
        public void onIntentSent(Intent intent) {
            if (intent.getAction().equals("android.media.action.IMAGE_CAPTURE")) {
                try {
                    Uri imageUri = intent.getParcelableExtra(MediaStore.EXTRA_OUTPUT);
                    Context context = InstrumentationRegistry.getTargetContext();
                    Bitmap icon = BitmapFactory.decodeResource(
                            context.getResources(),
                            R.mipmap.ic_launcher);
                    OutputStream out = getTargetContext().getContentResolver().openOutputStream(imageUri);
                    icon.compress(Bitmap.CompressFormat.JPEG, 100, out);
                    out.flush();
                    out.close();
                } catch (IOException e) {
                    GenericUtility.handleException(e);
                }
            }
        }
    };
    IntentMonitorRegistry.getInstance().addIntentCallback(intentCallback);

 //Perform action here
 onView(withId(R.id.tv_take_photo)).perform(click());

You need to create a IntentCallback to intercept the Uri value and save a sample image there.

Sample in Kotlin

  intentCallback = IntentCallback {
        if (it.action == "android.media.action.IMAGE_CAPTURE") {
            it.extras.getParcelable<Uri>("output").run {
                val inStream = Resources.getResource(sampleImageFileName).openStream()
                val outStream = activity.contentResolver.openOutputStream(this)
                ByteStreams.copy(inStream, outStream)
            }
        }
    }

You need to register your callback before the intent trigger event

  IntentMonitorRegistry.getInstance().addIntentCallback(intentCallback)

And don't to forget to unregister at end

  IntentMonitorRegistry.getInstance().removeIntentCallback(intentCallback)

Ismael and Gupta's answers are correct. For those who want a complete example, I made a complete solution based on their example in Kotlin. The code below takes photo for multiple imageView and also verifies if the correct image is loaded in the respective imageview by checking the imageView.tag property. From the development code, the image name has to be set in the imageView.setTag(imageName)

private var imageName = "No Image Name"

@Test
fun verifyPhotoTaken() {
    intending(hasAction(MediaStore.ACTION_IMAGE_CAPTURE)).respondWith(
            ActivityResult(Activity.RESULT_OK, null))

    takePhoto(R.id.imageview1, R.drawable.ic_launcher)
    takePhoto(R.id.imageview2, R.drawable.some_image)
}

private fun takePhoto(imageViewId : Int, resourceId : Int) {
    val cameraIntentCallback = intentCallback(resourceId)
    IntentMonitorRegistry.getInstance().addIntentCallback(cameraIntentCallback)
    onView(withId(imageViewId)).perform(click())
    onView(withId(imageViewId)).check(matches(hasDrawable(imageName)))
    IntentMonitorRegistry.getInstance().removeIntentCallback(cameraIntentCallback)
}

private fun intentCallback(resourceId : Int = R.drawable.ic_launcher) :IntentCallback  {
    return IntentCallback {
        if (it.action == MediaStore.ACTION_IMAGE_CAPTURE) {
            it.extras?.getParcelable<Uri>(MediaStore.EXTRA_OUTPUT).run {
                imageName = File(it.getParcelableExtra<Parcelable>(MediaStore.EXTRA_OUTPUT).toString()).name
                val context : Context = InstrumentationRegistry.getInstrumentation().targetContext
                val outStream = context.contentResolver.openOutputStream(this)
                val bitmap : Bitmap = BitmapFactory.decodeResource(context.resources, resourceId)
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream)
            }
        }
    }
}

fun hasDrawable(drawableResource: String) : BoundedMatcher<View, ImageView> {
    return object : BoundedMatcher<View, ImageView> (ImageView::class.java) {
        override fun describeTo(description: Description?) {
            description?.appendText("has drawable")
        }

        override fun matchesSafely(item: ImageView?): Boolean {
            return item?.drawable != null && item.tag.toString().contains(drawableResource)
        }

    }
}
Related