Facebook`s screenshot tests failing with api bigger than 23 - android

I am using Karumi's shot plugin (https://github.com/karumi/shot) to take screenshots from my tests and compare then using facebook's library: http://facebook.github.io/screenshot-tests-for-android/
The library has an issue when running with api bigger then 23 because it needs the WRITE_EXTERNAL_STORAGE permission and since api 23, granting permissions during tests is not a trivial task.
But in espresso 3.0 was added the GrantPermissionRule and with this, you can set permissions previously to the execution of the test easily.
Well, I added the Rule:
#Rule #JvmField
val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
And took the screenshot with the following code:
Screenshot.snapActivity(activityTestRule.activity).record()
I have a custom TestRunner that runs:
override fun onCreate(args: Bundle) {
super.onCreate(args)
ScreenshotRunner.onCreate(this, args)
}
override fun finish(resultCode: Int, results: Bundle) {
ScreenshotRunner.onDestroy()
super.finish(resultCode, results)
}
But when I execute the test I receive the following error:
java.lang.RuntimeException: Failed to create the directory for screenshots. Is your sdcard directory read-only?
at com.facebook.testing.screenshot.internal.ScreenshotDirectories.getSdcardDir(ScreenshotDirectories.java:66)

The plugin fails trying to save the screenshots in an API >= 23 because the permission has to be granted in the testing APK and not the APK under test. Using the rule named grant permission test rule does not provide this functionality. This is not supported by the official Facebook library and we don't support it for now :(
I've also answered your question in the GitHub repository https://github.com/Karumi/Shot/issues/19#issuecomment-328334528

Try to use android:requestLegacyExternalStorage="true" in your manifest during test.
Dont forget to remove it once you finished

Related

Android GrantPermissionRule does not work with api-27

I've the permission rule configured as below in my MainActivityTest class
#Rule
public GrantPermissionRule permissionRule =
GrantPermissionRule.grant(RECORD_AUDIO, WRITE_EXTERNAL_STORAGE);
When I run below command to execute the tests on emulator with api 27
./gradlew connectedCheck
It fails with the below error
com.example.myapplication.MainActivityTest > testLaunch_main_activity[Pixel_XL_API_27(AVD) - 8.1.0] FAILED
androidx.test.espresso.NoActivityResumedException: No activities in stage RESUMED. Did you forget to launch the activity. (test.getActivity() or similar)?
at dalvik.system.VMStack.getThreadStackTrace(Native Method)
Surprisingly the permissions are showing as granted in the app info settings, but still its asking for permissions when the test is run on emulator with api version 27 (or lower)
Can someone please confirm if it is a bug in some android plugin or if I am missing anything here.
Source Code - https://github.com/vivekweb2013/test-android-project
You are using ContextCompat's checkSelfPermission to check whether the app has permission or not. This is backward compatible with the support libraries but not sure with androidx. Alternative to this can be to use PermissionChecker's checkSelfPermission api like,
For API level 22 and below,
int permission = PermissionChecker.checkSelfPermission(context, permission);
if (permission == PermissionChecker.PERMISSION_GRANTED) {
// permission is granted
} else {
// permission not granted
}
But given that these permissions RECORD_AUDIO and WRITE_EXTERNAL_STORAGE are dangerous permission which requires acknowledgement from the user before our app can start consuming it. Below API level 23, these permissions are granted automatically when it is declared in AndroidManifest so the other way to get rid of this issue can be to verify it only for API level 23+ since it makes sense to validate.
I'd suggest you use a RuleChain with an outer rule for permissions around the ActivityScenarioRule
The problem seems to come from a race between launching the activity through the scenario rule and the permission rule, with a RuleChain in place the order of execution becomes explicit and the behavior should be as expected.
Here's the updated code from your example:
public class MainActivityTest {
public GrantPermissionRule permissionRule =
GrantPermissionRule.grant(RECORD_AUDIO, WRITE_EXTERNAL_STORAGE);
public ActivityScenarioRule<MainActivity> rule = new ActivityScenarioRule<>(MainActivity.class);
#Rule
public RuleChain chain = RuleChain.outerRule(permissionRule).around(rule);
#Test
public void testLaunch_main_activity() {
onView(withId(R.id.txt_view)).check(matches(isDisplayed()));
}
}

android - what does it mean to override packages?

To be clear on what i am asking i will provide a real world example. take look at this and notice the following section:
Hotline - Android SDK Integration Steps Modified on: Fri, 6 Oct, 2017 at 8:21 PM
Integrate Hotline SDK (Using Gradle) Pre Requisites :
Hotline SDK clients require devices running Android 2.3 or higher
Hotline App Id and App Key from here: Where to find App ID and App Key
Android Studio and Gradle
If you have any queries during the integration, please send it to us - Submit a Query
1. Add Hotline SDK to your app
Add the maven URL to the root build.gradle (project/build.gradle)
allprojects {
repositories {
jcenter()
maven { url "https://jitpack.io" }
}
}
Add the following dependency to your app module's build.gradle file
(project/app/build.gradle):
apply plugin: 'com.android.application'
android {
// ...
}
dependencies {
// ...
compile 'com.github.freshdesk:hotline-android:1.2.+'
}
1.1 Android target version supported
Hotline SDK supports apps targeting Android version 5.0+. The SDK
itself is compatible all the way down to Gingerbread (API Level 10).
When app targets Android 7.0+
When FileProvider is not configured for Hotline SDK, the following
error code is displayed
"Missing/Bad FileProvider for Hotline. Camera capture will fail in devices running Nougat or later versions of OS (error code 354)"
To fix this, please include the provider in the
AndroidManifest.xml as below and specify the authority in strings.xml.
Assuming, com.example.demoapp is the package name of your app, the
declaration would be
AndroidManifest.xml
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.demoapp.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/hotline_file_provider_paths" />
</provider>
Strings.xml
<string name="hotline_file_provider_authority">com.example.demoapp.provider</string>
When app targets Android 8.0+
When the app's target is Android 8.0 or later, and by extension includes appcompat-v7 r26.0.0.+, you'll see the following errors
E/UncaughtException: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/freshdesk/hotline/activity/InterstitialActivity;
Hotline SDK's activities extends ActionBarActivity to keep the SDK
compatible with app's targeting older Android versions/appcompat-v7
revisions. It can be resolved by adding a proxy class
(ActionBarActivity was replaced by AppCompatActivity and was proxied
by lib itself since 24.2.0 of appcomapt-v7, until it was removed in
26.0.0) manually if you are building with support library 26.x.x.
Add the following class in the appropriate package
package android.support.v7.app;
public class ActionBarActivity extends AppCompatActivity {
}
my question has nothing to do with Hotline. But after i did what they asked my package structure looks like this:
now that you have some background let me tell you what i dont understand. Does this mean that i am overriding any calls in package android.support.v7.app.ActionBarActivity ? so does this mean that for any 3rd party build i have i can override its classes this way as long as i know the package and class name ?
Basically what does it mean to put package name of something i do not own into my package structure ? what does it do ?
UPDATE: look at this article here as another example . if you read solution 3 you see we can do the same thing with facebook.login. i personally implemented this and it works. my test package structure looks like this and it overrides facebooks loginCreator etc:
even though i implemented it, i still dont get whats happening. can someone explain ?

AndroidHttpClient not found (when running Robolectric)

I've set up a very simple project to test the integration of Robolectric + Data Binding + Retrolambda. When I run the test suit, I get the following message:
Error:(30, 30) Gradle: error: cannot access AndroidHttpClient
class file for android.net.http.AndroidHttpClient not found
This is pretty odd since I don't use AndroidHttpClient anywhere.
The error occurs here, on the "activity" line:
#Before
public void setup() {
activity = Robolectric.setupActivity(MainActivity.class); // Error on this line
textView = (TextView) shadowOf(activity).findViewById(R.id.textView);
button = (Button) activity.findViewById(R.id.button);
editText = (EditText) activity.findViewById(R.id.editText);
}
The program never uses AndroidHttpClient. In fact, this is the entire program:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setUser(new User());
binding.button.setOnClickListener((v) -> {
binding.textView.setText(String.format("Hello, %s!", binding.editText.getText()));
binding.editText.setText("");
});
}
Ideas as to what's wrong?
AndroidHttpClient was removed from the SDK in v23 of the build tools.
As Robolectric is running against earlier versions, it expects it to be there, which is why you're seeing this error.
For now, you can add it back in:
android {
useLibrary 'org.apache.http.legacy'
}
As detailed here.
There is a GitHub ticket open for Robolectric to fix this. You can follow the thread/ticket here.
Update:
As some people have correctly pointed out, a better way of doing this would be to create a class android.net.http.AndroidHttpClient in your test resources. This would be a preferred method because you're only modifying the test sources, not the production code, in order to accommodate the tests.
I've just added fake class android.net.http.AndroidHttpClient in my test sources. And it solved the issue for now. Waiting for Robolectric to be updated
Apparent problem and solution:
AndroidHttpClient was removed from the SDK in API Level 23, while Robolectric was set to run tests with SDK 21:
AndroidHttpClient was removed from the SDK in API Level 23
I was able to solve this problem by creating a new class called AndroidHttpClient within a new package android.net.http. After that I had to annotate my Unit Test class with #Config(constants = BuildConfig.class, sdks = 21) which will run the tests against an emulated version of API 21 which is the last version of Android Robolectric supports currently.
There is currently an issue opened here, so once they release version 3.1 everything should be fine and you won't have to use this workaround.
If target SDK is 28 or greater then according to this, we have to put following line in AndroidManifest.xml
<uses-library android:name="org.apache.http.legacy" android:required="false"/>

Android Studio: grant permission between installing test APK and running tests with graphical test runner

I'm trying to automate the disabling of animations as described in this post, but that only seems to work for command-line invocation of connectedAndroidTest. I want to use the graphical test runner in Studio, with the list box showing passed/failed tests. With that runner, the permission grant (adb shell pm grant ... android.permission.SET_ANIMATION_SCALE) is never run, seemingly because the gradle task installDebugAndroidTest is never run, instead the runner is running Gradle as far as assembleDebugAndroidTest (or whatever alternate gradle task I specify in my run configuration), and then installing com.mypackage.test by some other (non-Gradle?) method immediately before running tests. So any prior permission grant is reset by that installation.
How can I grant SET_ANIMATION_SCALE between the graphical test runner's installation of the test package and the running of the test?
You can do it using reflection, adding the permission to the manifest, creating an Espresso TestRule and a task (explained here in detail).
Add the permission to the manifest of a debug/mock variant:
<uses-permission android:name="android.permission.SET_ANIMATION_SCALE"/>
Create your own task depending on installDebug and make connectedDebugAndroidTest depend on your task. You also need to grant the SET_ANIMATION_SCALE permission for testing.
Create a test rule that uses internally reflection to retrieve and restore animation scales (code):
public class AnimationAwareWonderTestRule extends AnimationAwareAwesomeTestRule {
private float[] mAnimationScales;
#Override
protected void before() throws Throwable {
mAnimationScales = AnimationAwareWonder.tryToRetrieveAndDisableAnimationsAndTransitions();
}
#Override
protected void after() throws Throwable {
AnimationAwareWonder.tryToRestoreAndEnableAnimationsAndTransitions(mAnimationScales);
}
}
It works but seems it's not possible at the moment to use this permission in MarshMallow.

Method setUp in android.test.AndroidTestCase not mocked

I'm trying to come to terms with the new unit test feature of Android Studio.
I've followed the instructions on http://tools.android.com/tech-docs/unit-testing-support. The description there explicitly mentions the 'Method ... not mocked' error and suggests to put the following into the build.gradle:
android {
// ...
testOptions {
unitTests.returnDefaultValues = true
}
}
This works in so far as the tests run when started from the command line with
gradlew test --continue
but not when I run the test class from Android Studio with rightclick -> run. This way, I get the same error again:
java.lang.RuntimeException: Method setUp in android.test.AndroidTestCase not mocked. See https://sites.google.com/a/android.com/tools/tech-docs/unit-testing-support for details.
at android.test.AndroidTestCase.setUp(AndroidTestCase.java)
at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:86)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Any ideas on how to solve this?
EDIT: The content of the test class doesn't really matter because the setUp of the test fails, I tried with the most simple class:
public class ContactFormToolTest extends AndroidTestCase {
public void testSOmething(){
assertEquals(false, true);
}
}
Also tried overriding setUp, makes no difference.
From: https://sites.google.com/a/android.com/tools/tech-docs/unit-testing-support#TOC-Method-...-not-mocked.-
The android.jar file that is used to run unit tests does not contain
any actual code - that is provided by the Android system image on real
devices. Instead, all methods throw exceptions (by default). This is
to make sure your unit tests only test your code and do not depend on
any particular behaviour of the Android platform (that you have not
explicitly mocked e.g. using Mockito). If that proves problematic, you
can add the snippet below to your build.gradle to change this
behavior:
android {
// ...
testOptions {
unitTests.returnDefaultValues = true
}
}
The new Unit Tests feature in Android Studio fakes the entire Android SDK so that you can run fast, Java-only tests, without needing to install your application on an Android device (this is similar to Robolectric). The general idea is that you mock all the responses from the Android SDK calls.
AndroidTestCase is used to run a test with the real Android SDK.
So, your issue is that you are trying to run an AndroidTestCase that depends on the Android SDK, but your test runner is launching the Unit Tests environment, which uses a fake Android SDK instead of a real one.
You need to choose one approach. If you want a pure unit test, then you probably should use a JUnit 4 test class instead of an AndroidTestCase. More instructions here:
https://developer.android.com/training/testing/unit-testing/local-unit-tests.html#build
As of SDK version 24, AndroidTestCase is deprecated
This class was deprecated in API level 24.
Use InstrumentationRegistry instead. New tests should be written using
the Android Testing Support Library.
You are supposed to use the Espresso framework for UI testing. There is a tutorial.

Categories

Resources