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(?) #244

Closed
originx opened this issue Oct 16, 2016 · 10 comments
Closed

Android support(?) #244

originx opened this issue Oct 16, 2016 · 10 comments

Comments

@originx
Copy link
Contributor

originx commented Oct 16, 2016

What would be necessary in terms of effort and technical details to switch from cglib to ByteBuddy.

Motivation for this would be running JGiven in Android Espresso UI tests on emulators.
(cglib is not supported on android JVM)

Thanks

@janschaefer
Copy link
Contributor

I didn't have a look at ByteBuddy yet, but that should be possible without much effort I guess. Could you provide a small example project with an Android Expresso UI test?

@originx
Copy link
Contributor Author

originx commented Oct 17, 2016

I will create an example project for Android Espresso test soon, in the meantime I forked and started introducing ByteBuddy.

https://github.com/originx/JGiven

I am having issues with inner static classes, already reached out to ByteBuddy developer, probably it is due to misuse of the lib.

(btw you can take a look at the lib looks very promising you can also easily extract all the annotations etc, with it in a clean way)

cheers

@originx
Copy link
Contributor Author

originx commented Oct 17, 2016

With the help of ByteBuddy dev :jgiven-core:test tests look green on:
https://github.com/originx/JGiven

Now only thing left for Android test running would be ability to instrument jgiven to pick which classloader to use AndroidClassLoadingStrategy, create a new directory on the device during test run for class loading:

That means , see StandaloneScenarionExecutor.java code for loading needs to be made adaptable based on the (J)VM on which is being run on:

 return new ByteBuddy()
                .subclass(stepsClass, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING)
                .method(any())
                .intercept(MethodDelegation.to(methodInterceptor))
                .make()
                .load(getClass().getClassLoader())
                .getLoaded()
                .newInstance();

Would look like:

//TestActivity <-- usually context of the screen being ran on the Android, **_needs to be provided for each test_**, I think the best would be to give a directory as a class rule for every test running on android to JGiven "runner"

 File file = TestActivity.this.getDir(RandomString.make(), Context.MODE_PRIVATE); <-- 
 return new ByteBuddy()
                .subclass(stepsClass, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING)
                .method(any())
                .intercept(MethodDelegation.to(methodInterceptor))
                .make()
                .load(TestActivity.class.getClassLoader(), new AndroidClassLoadingStrategy(file))
                .getLoaded()
                .newInstance();

For Android class loading to work following needs to be introduced yet:

  • byte-buddy-android module needs to be included in the build
  • a folder for writing temporary files and compiled class files needs to be created on the android device when test runner starts
  • AndroidClassLoadingStrategy needs to be used as an argument in load(ClassLoader classLoader, ClassLoadingStrategy classLoadingStrategy); method (see above)

This is an example of a test with bytebuddy on Android
https://github.com/raphw/byte-buddy/blob/master/byte-buddy-android-test/src/main/java/net/bytebuddy/android/test/TestActivity.java

What would be best course of action to refactor the StandaloneScenarioExecutor so it can be instructed which class loader to use and to provide him a JUnit test rule / class test rule and to get a Context or File path for the test execution?

@janschaefer
Copy link
Contributor

janschaefer commented Oct 17, 2016

Looks good so far :-). But could you explain to me why 3 intercept methods are needed?
Regarding the file path: does it only have to be a random path? Maybe we can get around that without having to provide a file?

@originx
Copy link
Contributor Author

originx commented Oct 17, 2016

I followed advice from this ticket (the ByteBuddy developer) how to intercept methods:
https://groups.google.com/d/msg/byte-buddy/nBuGgkW63vQ/h9me2yPdYMYJ
He gave a thorough explanation why 3 methods, if you believe this is the wrong approach we can change it.

Regarding the path, I dont believe it needs to be random, maybe it would be smart to clean it up after the test run, technically it is just an empty folder for rw operations during class loading.
In Android world you need to have a "Context" of the application/activity so you can write on the internal storage of the application.

I scoured the net a bit and found something how Mockito does this:
https://github.com/mockito/mockito/blob/release/2.x/src/main/java/org/mockito/plugins/MockMaker.java

And actual use of the proxy bytecode classloader on android for Mockito is just a simple include of a library that contains the implementation of that MockMaker.
https://github.com/crittercism/dexmaker/blob/master/mockito/src/main/java/com/android/dx/mockito/DexmakerMockMaker.java

https://github.com/crittercism/dexmaker

Maybe this is the way to go, or if you have any ideas how to make it simpler?

cheers

@janschaefer
Copy link
Contributor

janschaefer commented Oct 17, 2016

That sounds good. My basic idea would be to create a simple artifact that you just depend on, like jgiven-android and then it basically works more or less out-of-the box. So additional dependencies would be no issue as you only get them if you depend on jgiven-android. The alternative would be to go for a reflection-based approach to avoid additional binary dependencies in jgiven-core. It is a bit like jgiven-spring, which also only provides some little helper classes that enable JGiven for Spring.

@originx
Copy link
Contributor Author

originx commented Oct 30, 2016

Yes lets try something like that, lately I dont have time to proceed working on it, but we will have to enable passing of the classloader of the started activity to the test runner once the test starts.
(also the empty folder that is created for classloading)

I think this will be the most tricky part passing those arguments after android "activity start" is called, which would effectively mean test run is in progress.

@originx
Copy link
Contributor Author

originx commented Nov 14, 2016

So I got most of the stuff running, since I am not completely familiar with JGiven project, code style etc. It would be good if you could take a look at the fork and put some commits/ents there if something feels off

I got a sample project with a JGiven test run on a emulator device, works fine, test runs and json report gets saved on the device <-- cache dir**, still not pulled to the reports of the build machine**

The challenge now will be pulling those files from the device to the reports dir in android, I have to research still how this is preformed then it will be mostly plug and play.

Usage would be extending the ScenarioTest and using 1 Junit rule
@Rule public AndroidJGivenTestRule androidJGivenTestRule =new AndroidJGivenTestRule(ScenarioTest scenarioTest);

Later on some improvements that could be done is actually just pulling the results from the device and doing heavy lifting on the JVM: parsing, converting, html generation etc. but MVP would be the goal atm

@janschaefer
Copy link
Contributor

Amazing! Supporting JGiven for Android is really a great new feature. I will have a look at the code as soon as possible.

@originx
Copy link
Contributor Author

originx commented Nov 27, 2016

#258 [Experimental] Android support PR created

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants