Shopify / android-testify

Add screenshots to your Android tests
https://testify.dev
MIT License
231 stars 23 forks source link

Screens which inflate views at a later time are never captured #245

Closed videogreg93 closed 2 years ago

videogreg93 commented 2 years ago

Describe the bug Regardless of which capture method I try, I can never actually capture anything on screen which either inflates views after creation or adds views to a recyclerview. Using Screenshot.capture() as a fourth capture method would fix this. Screenshot.capture() gives you access to a bitmap so comparing shouldn't been an issue.

This issue relates to:

To Reproduce Try to screenshot any activity with just a recyclerview into which you add views after onCreate of the  Activity.

Expected behavior Screenshot is correctly taken.

Screenshots If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

Target Android Device (please complete the following information):

Additional context Add any other context about the problem here.

DanielJette commented 2 years ago

Hi @videogreg93 thanks for the bug report Testify will capture the screen immediately after the screen goes idle as it assumes that, once the UI thread is idle, that all rendering is completed.

In order to allow for dynamic content to be loaded later, Testify will internally synchronize with the [Espresso onIdle() method]( https://developer.android.com/reference/androidx/test/espresso/Espresso#onIdle()). In a scenario such as you've described, I would recommend you define an Espresso idling resource which you can use to signal to Testify that you've completed loading your content.

videogreg93 commented 2 years ago

Hey there, thanks for tip! I tried adding a simple idling resource which just waits a couple of seconds to see if that would work, but even then while the screen is clearly finished rendering, this is the resulting screenshot. blank Hard to say exactly what's going on, the only thing I know is that using Screenshot.capture() correctly captures the view, while these methods do not.

DanielJette commented 2 years ago

@videogreg93 I was thinking about this problem a little more last night and I'm wondering if you might be running in to a GPU-acceleration problem. Testify's default capture method is not capable of capturing SurfaceViews, such as Google MapView or Jetpack Compose elements. Some other graphical elements such as elevation/shadows are also not captured since they're not present in the Bitmap buffer of the Activity, but rather contained in hard-accelerated surfaces. This means that the default Testify capture methods are not able to "see" that content. In order to capture SurfaceView contents, you will need to use an alternative capture method that is capable of capturing SurfaceViews.

I would recommend you try enabling PixelCopyCapture:

import com.shopify.testify.TestifyFeatures.PixelCopyCapture

@ScreenshotInstrumentation
@Test
fun default() {
    rule
        .withExperimentalFeatureEnabled(PixelCopyCapture)
        .assertSame()
}

PixelCopy grabs the content directly from the GPU on your device, so it's quite sensitive to hardware differences. As such, I recommend you also enable a lower exactness threshold for the matching using setExactness().

import com.shopify.testify.TestifyFeatures.PixelCopyCapture

@ScreenshotInstrumentation
@Test
fun default() {
    rule
        .withExperimentalFeatureEnabled(PixelCopyCapture)
        .setExactness(0.9f)
        .assertSame()
}

You can find an example of this here

It's possible that this capture method will work for your tests.

DanielJette commented 2 years ago

Moved to https://github.com/ndtp/android-testify/issues/91