Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android Support #96

Closed
saikrishna321 opened this issue Mar 7, 2017 · 74 comments
Closed

Android Support #96

saikrishna321 opened this issue Mar 7, 2017 · 74 comments

Comments

@saikrishna321
Copy link

Is there any ETA for android support as react-native apps support both iOS and android

@rotemmiz
Copy link
Member

rotemmiz commented Mar 7, 2017

No ETA yet, we are in the process of finishing and stabilizing detox for iOS. Android will follow.

@saikrishna321
Copy link
Author

@rotemmiz any specific tool detox would depend on like espresso ?

@rotemmiz
Copy link
Member

The plan is to use espresso, yes

@jeduden
Copy link

jeduden commented Mar 22, 2017

@rotemmiz is there a rough implementation plan ? would you consider accepting pull requests helping to get android support working faster ?

@rotemmiz
Copy link
Member

There's a plan indeed. I think it will be best if I write a bit about our roadmap in the documentation.

In a nutshell, we want to use a similar architecture as the one we implemented on iOS:

  1. use Espresso as the native driver (we'll need to figure out how to integrate with espresso, since it runs only with instrumentation context)- there's already a rough native skeleton implemented. the invocation mechanism is already implemented in react-native-invoke.
  2. wrap android and adb for emulator control - like we did with fbsimctl.
  3. extend Device.js to AndroidDevice and AndroidEmulator, implementing all of its functionality.
  4. The hardest part will be creating a custom IdlingResource for react native bridge, like we did in iOS. Since java can't swizzle functions, we'll need to use a different approach.

@rotemmiz
Copy link
Member

And of course, we love PRs :)

@DanielZlotin
Copy link
Contributor

for future reference: I managed to make this almost work, look here

Although I gave up on this when trying to test application lifecycle, as Espresso runs as part of the app under test's process, so stopping one will stop the other.
I ended up using uiAutomator with great results

@rotemmiz
Copy link
Member

I'm a bit afraid of trusting uiautomator , I had experience with it in MagnetoTesting, and it was flaky, we had to pet our UI tests constantly.
The problem you raised might not be an issue as the test runner in detox is external to the application and to espresso's lifecycle and resides in a different process.

@DanielZlotin
Copy link
Contributor

if you look at the current tests in https://github.com/wix/react-native-navigation/tree/v2/AndroidE2E/app/src/androidTest/java/com/reactnativenavigation/e2e/androide2e you'll see there's zero flakiness, it's just a little different kind of thinking than espresso

@talarari
Copy link

talarari commented Apr 2, 2017

also its worth noting that uiAutomator lets you interact with stuff outside the app.
so pulling down the notification curtain and such would work.

@simonracz
Copy link
Contributor

Hey, I plan to work on 2. and 3. from @rotemmiz 's list.

Is anyone else working on this?

Few notes:

  • android is deprecated, was split to sdkmanager and avdmanager
  • platform_tools can be installed now separately, which is nice
  • adb and avdmanager seems to be enough alone, they cover the current use cases

@simonracz
Copy link
Contributor

Which one of the following would be preferred?

  1. Call adb directly similarly to how fbsimctl is called.
  2. Use adbkit for adb commands.

The 2. option adds another dependency, might not cover everything, but on the upside is already tested. I would prefer 1. at the moment, but really don't mind.

@talarari
Copy link

@rotemmiz - can you elaborate on this please?

  1. The hardest part will be creating a custom IdlingResource for react native bridge, like we did in iOS. Since java can't swizzle functions, we'll need to use a different approach.

@LeoNatan
Copy link
Contributor

@talarari What I did for iOS was to strategically swizzle RN methods so that I could react to what is happening for internal logic, such as starting a timer or the JS thread runloop.

Method swizzling is the process of replacing a method implementation at runtime. Since Objective C is dynamic, there is a great flexibility regarding this. Java does not allow method swizzling, so it would be much more difficult to implement a similar approach.

@LeoNatan
Copy link
Contributor

LeoNatan commented Apr 12, 2017

@rotemmiz
Copy link
Member

Thanks for the references @LeoNatan, I like where this is going :)

@talarari , @simonracz , I will post our roadmap for detox soon, along with how we thought about implementing support for Android.

@simonracz
Copy link
Contributor

Just to keep the ball rolling.

I have a proof of concept code for a custom Android test runner. It is actually a subclass of MonitoringInstrumentation.

It gets the detoxServer and detoxSessionId arguments from cmd line (or from Gradle settings). Can be started like adb shell am instrument -w -e detoxServer ws://localhost:8001 -e detoxSessionId 1 com.example.detoxsample.test/com.detox.DetoxRunner.

It opens a websocket to the detox server, logins to it and then starts listening to commands. From this point it can call, .e.g Espresso methods using reflection as it has the permission to poke the app in any way it wants.

Some open problems for me:

  • Espresso's main selling point is it's automatic detection of idleness. For this, custom async code must notify Espresso about it's work. E.g. okhttp-idling-resource This must be added for react native.
  • Espresso needs AndroidJUnitRunner. Subclassing this was a not an option. So, I have to add it's functionality to the custom test runner, which is a bit messy.
  • React Native transforms testids to view tags on Android. This is okay for Espresso, but won't work for UIAutomator. (UIAutomator is needed for testing notifications.)

@ptomasroos
Copy link

Great news. Looking fwd to a follow up!

@rotemmiz
Copy link
Member

@simonracz this is great!! can you please share the code ?

regarding your points:

  1. it would be easiest to add okhttp-idling-resource to react-native, but I want to start trying the patch art vtable (this would be very useful for other features, like the RN bridge idling-resource).
  2. Did you have a look at my initial skeleton for Android ?(https://github.com/wix/detox/blob/master/detox/android/detox/src/main/java/com/wix/detox/DetoxManager.java). It uses instrumentation, and when the single instrumentation test is initiated, detox adds a looper to that thread, forcing it to wait for network requests. I'm not sure this is the right solution yet, but it saves the need to run a custom test runner.
  3. I agree, although the issue with UIAutomator should be in a lower priority, we can send a PR to RN that adds testIDs to content-description as well.

@simonracz
Copy link
Contributor

@rotemmiz I'll upload the code tomorrow morning.

  1. Yes, I did, but completely glanced over the Looper. Were you able to test this approach? It seems simpler than mine.

I am staring at the AndroidJUnitRunner code for half an hour now and can't remember why I made the conclusion that it can't be subclassed for this purpose. So, please ignore my earlier comment about that.

@simonracz
Copy link
Contributor

One thing that goes for the custom test runner is that that is the only way to run code before Application#onCreate().

@simonracz
Copy link
Contributor

I've uploaded the code here.

@talarari
Copy link

Regarding point 3:
we use accessibilityLabel as well as test id.
React native transforms it to content description

@simonracz
Copy link
Contributor

simonracz commented May 1, 2017

I delved deep into RN's Android port while keeping in mind Detox's iOS support.

At first, I thought of using Espresso's internal QueueInterrogator class for idle detection. It would work fine, as while RN's core classes both have Java and mirror C++ parts, threading is done in a JVM aware style, only in the Java layer. The four relevant MessageQueues to be observed can be found here. The Loopers could be caught through the ReactContext for them. This is a valid approach, but I've found a better one.

RN's Android port seems to be more mature than the iOS port. They have a vertically implemented addBridgeIdleDebugListener function in CatalystInstance. They also have an idle detection utility that is based on this here.

The waitForBridgeAndUIIdle method could be used by Detox. This still needs to be wrapped for Espresso, but that's not an issue.

The only thing that is missing, is that it does not monitor the Timers the same way as Detox does. It just waits 2 frames of Choreographer, then marks it idle. (Choreographer is similar to CADisplayLink on iOS.) However, the timers are orthogonal to this.

Waiting for the Timers "the Detox way", could be done by getting hold of the mTimers variable using reflection, and checking whether it's empty or not. So, there is no need to attach to method calls, like on iOS.

I'll work on a proof of concept code and will check the idle detection manually with Detox's test. I'll share the code afterwards. Any feedback is welcome.

@rotemmiz
Copy link
Member

rotemmiz commented May 1, 2017

Hey @simonracz, please check your Twitter ;)

@AlanFoster
Copy link
Contributor

Adding to the discussion; I wonder if there will be an issue in providing full android test support without testID being available within android? 🤔

@itsmepetrov
Copy link

BTW, the long discussion about testID in Android: facebook/react-native#9942

@simonracz
Copy link
Contributor

Hey, @itsmepetrov
I am familiar with that thread. Pretty sad. That's my main reason, why I don't really consider PRs to React Native.

Test ids are transformed to view tags on Android. Espresso can use view tags. UIAutomator can't.

I think tags will be okay for most of the cases.

@simonracz
Copy link
Contributor

@AlanFoster You can find an RN view with testID='hello_button' the following way.
Espresso.onView(withTagValue(is((Object)"hello_button"))).check(matches(isDisplayed()));

DanielMSchmidt pushed a commit that referenced this issue Oct 5, 2017
This should enable other users to use android more
easily as the current status configuration can be
quite confusing.

Build with the help of @AppsThatMatter

Relates to #96
DanielMSchmidt pushed a commit that referenced this issue Oct 5, 2017
This should enable other users to use android more
easily as the current status configuration can be
quite confusing.

Build with the help of @AppsThatMatter

Relates to #96
simonracz pushed a commit that referenced this issue Oct 5, 2017
This should enable other users to use android more
easily as the current status configuration can be
quite confusing.

Build with the help of @AppsThatMatter

Relates to #96
@vocampos
Copy link

I'm going through the problem below when running the tests.

My Settings (package.json)

"detox": "^5.8.1",
"react": "15.4.2",
"react-native": "0.41.2",
"mocha": "^4.0.1",

 "detox": {
    "configurations": {
      "android.emu.debug": {
        "binaryPath": "android/app/build/outputs/apk/app-debug.apk",
        "build": "pushd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug && popd",
        "type": "android.emulator",
        "name": "Nexus_5X_API_25",
        "session": {
          "server": "ws://localhost:8099",
          "sessionId": “appName”
        }
      }
    }
  }

Test (firsTest.spec.js)


describe('Example', () => {
  beforeEach(async () => {
    try {
      await device.reloadReactNative();
    } catch (error) {}
  });

  it('should show world screen after tap', function() {
    element(by.text('Say World')).tap();
    expect(element(by.text('World!!!'))).toBeVisible();
  });
});

Command executed:


 detox test --configuration android.emu.debug --loglevel info

Result

"after all" hook:
Error: Timeout of 120000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.

Any suggestion?

@simonracz
Copy link
Contributor

Hey @vocampos ,

Could you please run an adb logcat in a terminal while you run your tests?
My guess is, that you will see something like “XY is busy” in the logs.

Please share those lines with me.

Cheers

@simonracz
Copy link
Contributor

The --debug-synchronization [ms] feature for Android has just been pushed to the master branch. It'll be in the next release.

Example usage

detox test -c android.emu.debug --debug-synchronization 500

This is the first tool you should consult, in case your tests are extremely slow or even timeouts. It reports back the busy IdlingResources that are holding back the test call.

@vocampos
Copy link

vocampos commented Oct 17, 2017

@simonracz ,

I ran the command according to its orientation, but it did not work.

When analyzing the adb logs, I have the below error:

--------- beginning of crash
10-17 14:54:38.384 3639-3677/? E/AndroidRuntime: FATAL EXCEPTION: com.wix.detox.manager
Process: com.app, PID: 3639
java.lang.RuntimeException: ReactInstanceManager is null
at com.wix.detox.ReactNativeSupport.waitForReactNativeLoad(ReactNativeSupport.java:159)
at com.wix.detox.DetoxManager.start(DetoxManager.java:64)
at com.wix.detox.Detox$1$1.run(Detox.java:133)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at com.wix.detox.Detox$1.run(Detox.java:136)
at java.lang.Thread.run(Thread.java:761)

Any suggestion?

Thank you

@vocampos
Copy link

The error is happening for detox.init not finished.

before(async () => {
    console.log('Start ');
    await detox.init(config.detox);  // not finished
    console.log('End');
});

1) "before all" hook: Error: Timeout of 120000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.

@simonracz
Copy link
Contributor

Hey @vocampos

Thanks for the extra info.

You just hit an error of the type that should never happen ™. :)

I've just noticed that you use RN 0.41.2. We never tested the Android version below 0.44.0. So, any bad thing could happen there. That's my current guess.

If you can reproduce it with later RN versions too, please let me know.

Regardless of that, I'll take a look whether I can support 0.41.2 too.
But, please be aware that under 0.45.0 we already know, that some things can't be supported, e.g. syncing with native animations.

@skv-headless
Copy link

How to match by type on android?
For example on ios by.type('RCTTextInput') is working find. On android I tried TextInput, ReactTextInput, EditText, ReactEditText nothing seems to work.

@simonracz
Copy link
Contributor

For Textinput you probably have to search by android.widget.EditText.

My guess is that the full classname was the missing part for you.

Let me know if it still doesn’t work.

@vasyas
Copy link
Contributor

vasyas commented Nov 3, 2017

Is Genymotion supported atm?

@ptomasroos
Copy link

@vasyas #96 (comment)

@ilonashub
Copy link

@simonracz hey, I have the same issue with running detox on Android. We got it working in iOS.
We use RN 0.49.
When I run detox on Android - I get a white screen, and in the console I get stuck in the "before all" hook:

 1) "before all" hook

  0 passing (3m)
  1 failing

  1) "before all" hook:
     Error: Timeout of 200000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.

I tried to look for stuff in the adb logcat, but didn't see any error with “XY is busy” like you mentioned.

Do you have any idea what might be the problem? Thanks!

@simonracz
Copy link
Contributor

simonracz commented Nov 9, 2017

Hey @ilonashub ,

It's hard to guess what went wrong.

Few random things, I would check.

  1. If your app is a hybrid app, then, is your detox android test (not the js, but the java test) starts the correct Activity, which starts RN?
  2. You should see something in the logs. In case there are no errors there at all, how far did your app managed to get? (It's very strange that you see only a white screen.)
  3. You can also start the detox server manually with detox run-server. I would check whether the android emulator also logs there, not just the js test runner.

I think it would be best to open a seperate issue, and please attach a chunk of adb logcat that you think might be relevant.

Simon

@simonracz
Copy link
Contributor

simonracz commented Nov 9, 2017

Hey @vasyas ,

@rotemmiz worked on this, but I think Genymotion is currently not supported. See here.

We should have an option, which just let's you use "whatever is visible via adb".

I plan to look at this. Was a bit busy recently with non-detox stuff.

@vasyas
Copy link
Contributor

vasyas commented Nov 9, 2017

@simonracz thanks.
I had already connected to Genymotion using the way you said ("whatever is visible via adb").
But now I'm getting a different message, see #386. Could you comment what else needs to be done to fix it?

Probably I can make it a pull request.

@ipinzari
Copy link

I m having a hard time getting detox working with Android, it can't connect with the Emulator. Is there a more detailed android Get Started where it goes step by step how to configure?

@rotemmiz
Copy link
Member

Not yet, they will be out when Android support gets ironed out. We are currently working on that.

@ipinzari
Copy link

ipinzari commented Nov 16, 2017 via email

@rotemmiz
Copy link
Member

It's an ongoing open source project effort. Android support is usable, and will continue to get better as we put lots of efforts in it.

You are welcome to open a specific issue with exact information regarding your errors, it might help us pinpoint issues we didn't yet find.

There will never be "estimate dates", especially to vague goals !

@Karniej
Copy link

Karniej commented Jan 18, 2018

Anyone got Detox + Jest working on Android with
"detox": "^7.0.0-alpha.0"
jest": "^22.1.0",
"babel-jest": "^22.1.0",
?
I am struggling with configuring it for more than a day today and nothing really helps. I just want to know if someone is actually using it.
I got it running on iOS pretty quickly...

Well I have moved from Genymotion to Android Studio 3 and it helped...

@llostris
Copy link

I'm also having issues setting it up for Android (using detox 7). When I run detox it uploads and starts the app, but then it hangs on the first, white screen (app is not responding)...

@rotemmiz
Copy link
Member

rotemmiz commented Feb 5, 2018

Android support is already pretty mature now, I suggest any problem will be handled in a separate issue.

Closing this issue.

@rotemmiz rotemmiz closed this as completed Feb 5, 2018
@wix wix locked and limited conversation to collaborators Jul 23, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests