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

Test execution much slower compared to Robolectric 1.x #982

Closed
mpost opened this issue Feb 25, 2014 · 26 comments
Closed

Test execution much slower compared to Robolectric 1.x #982

mpost opened this issue Feb 25, 2014 · 26 comments

Comments

@mpost
Copy link

mpost commented Feb 25, 2014

We have quite a few robolectric tests that we migrated from robolectric 1.x to robolectric 2 (2.3-SNAPSHOT). We witnessed that the test execution is much slower compared to robolectric 1.x.

Our 2000+ tests take 3-4 times as long to execute. Eg from 30-40 seconds up to 2 minutes and more. Combined with the gradle startup time the development experience is subpar.

Can we expect to see better robolectric performance in the future?

Is there anything that can be done to speed up the execution? Or any robolectric patterns to avoid?

@JakeWharton
Copy link
Member

You can paralellize your JUnit test runner. You can also parallelize Gradle invocation with a CLI flag. Slower tests are the price we pay for using real code instead of the (abysmal) fake code from version 1.

@mpost
Copy link
Author

mpost commented Feb 25, 2014

That sounds interesting. Could you point to any specific switches to enable parallel execution?

@JakeWharton
Copy link
Member

For Gradle it's just --parallel. JUnit is a bit more complicated. There's a few tutorials you can Google, though.

@mpost
Copy link
Author

mpost commented Feb 25, 2014

Ok, I'll experiment with it and report back.

@erd
Copy link
Member

erd commented Feb 25, 2014

Also, keep in mind that you only need to use the Robolectric test runner for tests that touch Android code. You can try and break down your dependencies enough such that you can use mocks to stand in for Android classes and use the normal JUnit test runner.

@mpost
Copy link
Author

mpost commented May 6, 2014

Sorry for bringing this issue up again but performance of test execution reached an all time low. I am running 2.3-SNAPSHOT on Windows 7 with the gradle-android-test-plugin and have experienced dramatic test execution speed reduction in the last few weeks.

A single robolectric test takes between 0.5 and 1 second. Running a few thousand tests is really becoming a problem when trying to get into a rapid development cycle. Especially since you can not reliably run a single test in AndroidStudio.

The --parallel flag is also not an option as it executes modules in parallel. Not tests themselfs.

@mpost
Copy link
Author

mpost commented May 6, 2014

It should be mentioned that the performance problems originates from the creation of a new activity in every test. That is a very slow operation.

@xian
Copy link
Member

xian commented May 6, 2014

Have you tried running the tests with a profiler to get an idea where the time is spent?

@christopherperry
Copy link

I'm seeing the same thing. I tried to mitigate the issue of Robolectric loading my actual Application class by overriding the test runner and always returning a vanilla Application (assuming all my setup code in there was causing the slowdown), but that did basically nothing. Still abysmally slow (489 test in 43s), and I'm on a fast machine.

I just tried parallel execution with the maven surefire plugin, and Robolectric barfs all over the place complaining about things not being on the main thread.

Caused by: java.lang.RuntimeException: you should only be calling this from the main thread!
    at org.robolectric.shadows.ShadowLooper.resetThreadLoopers(ShadowLooper.java:48)
    at org.robolectric.Robolectric.reset(Robolectric.java:1347)
    at org.robolectric.internal.ParallelUniverse.resetStaticState(ParallelUniverse.java:47)
    at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:226)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
    at org.apache.maven.surefire.junitcore.pc.Scheduler$1.run(Scheduler.java:258)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:744)

@jdmunro
Copy link

jdmunro commented Jul 29, 2014

Does anybody have anything more to add to this topic? I am experiencing similar issues. As @mpost mentioned it takes typically 0.5-1s for creating an activity. For apps with several activities and tests this soon adds up to a huge bottleneck and pretty much stops the ability to TDD activities.

@JakeWharton
Copy link
Member

Correctness is more important than speed as we learned from version 1.

If it's causing problems, profile, optimize, and send pull requests. Robolectric is a community project.

@jdmunro
Copy link

jdmunro commented Jul 29, 2014

Thanks for the quick response - contributing is something I will consider. At the moment I just wanted to check whether this was a known bottleneck or perhaps indicative of incorrect usage of the ActivityController, for instance. Also, apologies, I hadn't noticed the "Slower tests are the price we pay for using real code instead of the (abysmal) fake code from version 1." comment above!

With regard to correctness, I fully agree. From a speed perspective, my concern there was that it may be prohibitive to a test-first style approach for developing activities, it is certainly a trade off.

@erd
Copy link
Member

erd commented Jul 29, 2014

For apps with several activities and tests this soon adds up to a huge bottleneck and pretty much stops the ability to TDD activities.

As someone who does TDD all day, every day, I definitely feel your pain. The only suggestion I would add is to see if there is anything you are testing in an activity that you could break out to a separate object and test it outside of an activity (and further from the Android UI framework). Driving an activity through all the necessary lifecycle methods does indeed slow things down a bit.

@jdmunro
Copy link

jdmunro commented Jul 30, 2014

@erd That's a good strategy. Actually, we're just looking into splitting the activity testing into a separate Gradle task altogether which we'll still run, but less often than the regular tests. Though I haven't yet had much luck as my Gradle-fu is still weak.

@mpost
Copy link
Author

mpost commented Jul 30, 2014

A strategy that we applied is to reuse the same activity instance throughout many test methods and even test classes. Thereby we speed up our tests quite a bit. Still there are cases where you need to create a new Activity where the speed penalty will hit you.

@jdmunro
Copy link

jdmunro commented Jul 30, 2014

@mpost I was trying something similar actually, do you reuse just the ActivityController or the actual Activity itself? In many of our cases we need the activity to be starting from a "clean" state - how are you handling that?

@mpost
Copy link
Author

mpost commented Jul 30, 2014

@jdmunro We are reusing the concrete Activity object. Often times we just need an Activity to instantiate other Views so we would not need to configure the Activity in a special way.

@kingargyle
Copy link
Contributor

@mpost I've gotten around the issue of even need to build the activity by using Mockito to mock it out, and then passing in the mock, or mocking out the findViewById method to return a mock view or inflate it directly. If you can avoid creating an activity for a unit test, do so.

@davcamer
Copy link

If you're using roboguice along with robolectric, the container setup can easily be the dominator for test speed. On a project where that was the case, @rcarragher made some changes to roboguice that allowed us to re-use the injector instances from one test to the next. Gave us approximately 30% improvement.

The discussion around it is here:
roboguice/roboguice#178 (comment)

On the same project, we had successfully parallelized the unit test run in maven using the surefire plugin. I think it was parallel at the suite level. I'll try to dig up the config, I need it again for a new project.

@erd
Copy link
Member

erd commented Nov 3, 2014

We've done a few things to help with performance in 2.4. Please re-open if you have more specific things in mind.

@erd erd closed this as completed Nov 3, 2014
@mpost
Copy link
Author

mpost commented Nov 4, 2014

While i hope to see a performance gain i just witched from 4.3 to 4.4-SNAPSHOT and i see a HUGHE performance drop. We have around 2500 robolectric tests and they went from 3 minutes to 7-8 minutes.

@ylogx
Copy link

ylogx commented Sep 18, 2015

Similar problem with 3.0. First test in every suite taking long time. It's like:
class AbcTest:
test1: 18sec
test2: 50ms
test3: 41ms . . .

Is this usual? I don't remember tests taking so much last time I worked with 2.4 tests! We used gradle to run tests back then. I am using junit now.

@bolatug
Copy link

bolatug commented Jan 12, 2017

My first test on newly created project and I am just trying to robolectric "writing your first test" example. It takes 16 seconds which scares me. Fast feed back is the first reason for TDD. Is there any thing I can do?

org.robolectric:robolectric:3.2.2
targetSdk 24
minSdk 15
my computer is window 8.1 with some Intel i7 processor and 16GB ram

@jongerrish
Copy link
Contributor

jongerrish commented Jan 12, 2017 via email

@bolatug
Copy link

bolatug commented Jan 12, 2017

I just created the project with an empty activity called MainActivity and just added a Button in to the layout. And created another activity and tried to implement the test case in the site http://robolectric.org/writing-a-test/
So there is only one unit test. And the only logic the project contains is calling activityStart method to open other empty activity.

I couldn't find any resources about how to profile a unit test, can any one provide me some pointers. I am using Android Studio 2.2.3

@mpost
Copy link
Author

mpost commented Jan 13, 2017

The instantiation of an Activity takes very very long. Therefore it is advised to reuse an activity throughout multiple tests.

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