I have a couple of simple tests, like assertNotNull(mActivity); (I'm reading M.D.Torres "Android Application Testing Guide"). The activity under test runs okay. Every single test runs okay as well. But if I run several tests at once on the second test getActivity() never returns. No errors in logcat (last line "Starting Intent ..."), no nothing. Debugging doesn't help much either, if I step into getActivity() it complains that there is no source code available.
Another test project - ActivityTesting from Google runs okay even with several tests, so Eclipse is configured right.
Did anybody ever encounter something like that?
I recreated test project once more (like "clean room") and it worked. Then I compared two projects and found the culprit. It was empty teardown:
protected void tearDown() throws Exception {
}
If I remove it, all tests run green. If I paste it back, second test hangs. Now I would like to read the explanation and ready to mark it as answer.
Edit: I should be calling super.tearDown() at the end of the tearDown method.
Sorry for bothering everybody.
I used ActivityInstrumentationTestCase2 to test an Activity using ExoPlayer and correct resource clean up as well. As the clean up and final checks are the same for all the tests, I thought that tearDown() is a good place to implement them. All the tests run separately without any problem, but when I run multiple tests, sometimes getActivity() did not return. My tearDown() implemented various things:
check state of activity (various assert() calls)
check state of player (various assert() calls)
clean up player resources manually (calling close() and release())
setActivity(null) (this caused the trouble)
super.tearDown()
I tried all the suggested workarounds like overriding getActivity() and using the other methods of instrumentation to create and clean up the activity. These methods did not help.
And lots of debugging showed, that with above scenario the onDestory() of the activity of the previous test can overlap with the onCreate() of the activity of the next test. So the logs showed the life cycle events in this order:
test1.getActivity();
test1.tearDown() called;
test1.tearDown() over;
test2.getActivity()
test2.onCreate();
test1.onStop(); --> why is this late?
test1.onDestroy(); --> why is this late?
test2.tearDown() called;
test2.tearDown() over;
test3.getActiviy() --> this should call test3.onCreate, but did not and never returned.
This happens even when the test cases are implemented in separate ActivityInstrumentationTestCase2 classes/files. And the fist time this overlap does not cause trouble yet, so 2 tests are always OK, but running 3 tests in any order that result in this overlap causes the 3rd call to getActivity() to never return.
I tried everything like calling onPause() + onStop() + onDestroy() manually using the instrumentation, introducing really long sleep periods between tests, force clearing the activity of the instrumentationTestCase, reordering checks of my tearDown, but nothing helped. Finally, I accidentally removed setActivity( null) before my checks, and the life cycle events got correctly ordered:
test1.tearDown() called;
test1.onStop();
test1.onDestroy();
test1.tearDown() over;
test2.getActivity()
test2.onCreate();
...
So what really made the difference in my case: do not call ActivityTestCase.setActivity(). This causes the super.tearDown() not to call the life cycle events of the activity directly, clean up will happen later and cause trouble.
Related
The Situation
In the official documentation here: https://google.github.io/android-testing-support-library/docs/rules/index.html, it says:
"This rule provides functional testing of a single activity. The
activity under test will be launched before each test annotated with
#Test and before any method annotated with #Before. It will be
terminated after the test is completed and all methods annotated with
#After are finished. The Activity under Test can be accessed during
your test by calling ActivityTestRule#getActivity()."
Technically yes, the Activity is being terminated. But there doesn't seem to be any guarantee as to when this will happen. E.g. it won't necessarily happen before it's created again for the next test.
The Problem
In some of my tests, I need to rely on the fragments OnDestroy or OnDetach being called after each test, before the next test starts. I have listeners that need to be cleared and recreated.
If onDestroy from the previous test is called after OnResume in the current test, then the callback is cleared and the view doesn't get updated and the test fails.
If onDestroy from the previous test is not called at all, then the callback from the current test will be referring to the wrong instance. Again the view will not get updated and the test will fail.
The Question
Is this behaviour discussed in the situation by design or is it a bug? I'm so far unable to find this in the documentation.
What is best practice to handle this? I suspect other people have faced this problem.
Edit: I've now solved part 2. See workarounds section below. However if someone can answer part one by citing an official resource then I'd be happy to accept that answer. That's what I'm really asking here. The second part was just a bonus if someone had some ideas.
The Proof
If you would like to see this behaviour it will only take a few moments. Create a new project with an Activity like this:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onResume() {
super.onResume();
}
#Override
protected void onDestroy() {
super.onDestroy();
}
}
and a test class like this:
#RunWith(AndroidJUnit4.class)
#LargeTest
public class EspressoLifecycleTest {
#Rule
public ActivityTestRule<MainActivity> mActivityRule =
new ActivityTestRule<>(MainActivity.class);
#Test
public void test1() {
}
#Test
public void test2() {
}
#Test
public void test3() {
}
#Test
public void test4() {
}
}
Put breakpoints on the OnResume and OnDestroy methods and run the test suite in debug mode.
Do this a few times and notice that the order the Activity life cycle methods are called is not consistent. E.g. it might call OnResume twice in a row, and then call OnDestroy once, and then OnResume twice again, and then OnDestroy three times, or any other combination you can think of. Of course it always starts with at least one OnResume. Sometimes it doesn't even call OnDestroy if it's at the very end, but that's fine. What's not fine is that my tests are flaky because of this unpredicatable order.
I'm aware that this might be intentional and there could be a simple way to deal with it, I'm just not lucky enough to have found it. Please if you know what it is, post the answer here. I don't care how dumb my question might be in hindsight, I've spent a LOT of time on this problem. It's almost always something simple so I'm prepared to be embarrassed by the answer.
Workarounds
Using onPause over OnDestroy has the side effect of being called when I startActivityForResult, but without calling onResume again in the background fragment while in tablet mode. I'm exploring ways to make this work but no solution as yet.
Edit: onPause ended up with the same problem - which is partly why I was using onDetach in the first place. Ultimately, there are times when I don't want to detach the listeners until the fragment is destroyed.
This leads me to my next idea which worked! Hooray! Up until now I was creating a callback for the calling Activity, for the thing it was asking for, only if that specific callback didn't exist. It turns out this was a bad idea. I did that so I could limit the number of callbacks to the exact number required. The motivation was sound but the implementation required all this callback clearing. The solution is to recreate every callback when ever it's called from the fragment. Don't create it if it's null, always create it and replace whatever was there before. Now there's no need to clear them at all (AFAIK).
It's a bug: http://b.android.com/201513
I use fork work around it: https://github.com/shazam/fork
Noticed this issue before and the 'solution' I can think of is to override methods in ActivityTestRule: afterActivityFinished() or beforeActivityLaunched(). Basically you want to check and wait the listeners are cleared before next test execution.
IMO, this is a bug of ActivityTestRule.
my test closes the app and any activities associated with it no matter if this is called or not. What is the point of it and is it safe to remove it from all of my test classes?
In my opinion, you shouldn't remove that method, especially, if you have more than one method in a test class. Every test method should be independent, only calling tearDown it can be achieved. According to documentation:
Make sure all resources are cleaned up and garbage collected before moving on to the next test. Subclasses that override this method should make sure they call super.tearDown() at the end of the overriding method.
Solo has method finishOpenedActivities, which should be called in tearDown. If you remove tearDown from your test classes only tearDown of parent class will be called and you may get unexpected issues related to unfinished activities in next test method.
I'm writing a unit tests for activity with animation. Animation is simple rotation of spinner drawable with infinite duration(splash screen).
When i start unit tests, every test seems to wait until animation is finished, so they stucks infinte, When i set duration to 1 it all passes, cause animation ends quickly.
How can I override that, so tests don't wait for animation ending?
I assume that you intend to test behavior other than the animation.
You will probably benefit the most from moving the animation off the path of the code you want to test. This means moving the other code--code that doesn't depend on the animation--out of the Activity. If not out of the Activity, then, at least out of the Activity lifecycle methods (onCreate(), ...). You can then check this behavior without running the behavior at all. Even better would be to run the behavior without starting the Activity at all (don't call onCreate()).
The problem you encountered is a special case of what happens when you put your code directly into a framework extension point: your code then becomes intermingled with the framework and you get stuck running something that you don't care about in order to run the part that you do care about. So don't do that. Put your code in classes that don't depend on the framework, then use the framework extension point (the Activity in this case) to connect Android to your code. The Activity knows that your code exists; your code remains blissfully ignorant of Android. Now things like your animation can't get in the way.
(Note that I've searched online for the warnings I'm describing below, and have come up with next to nothing about them.)
I'm working with API level 10. I have a preference screen (XML-based), and one of the options in there creates a custom ListActivity as follows:
PreferenceActivity contains an option that creates a...
ListActivity which is a dialog that employs...
setOnClickListener() which contains an onClick() method that (right before calling finish()) will startActivity() a new Intent...
sub-Activity which starts up an...
AsyncTask which does variable time work which when done calls...
onPostExecute() which calls finish()
The thing is, it works... but I'm getting a raft of warning starting with:
10-16 21:59:25.010: WARN/WindowManager(170): Rebuild removed 4 windows but added 3
10-16 21:59:25.010: WARN/WindowManager(170): This window was lost:.....
Curiously, this raft of warnings ONLY comes up when the task executes quickly! When I added a Thread.sleep() call to my AsyncTask to artificially inflate its runtime it worked and threw no warnings whatsoever. In fact, as long as it takes more than (roughly) 500 ms to run it works fine. (Note that I tried using startActivityForResult() to no greater effect - the same problem occurs.)
The goal is that the user selects a preference item, they change its setting, some processing takes place, and then the user is left back at the preference menu they started on.
I'm betting it's a race condition... the order in which the windows are destroyed varies depending on that run-time... and I get the impression that when the sub-Activity closes before its parent ListActivity the warnings get thrown. But sprinkling a 1s sleep() in isn't a reasonable solution unless this is some sort of Android bug (unlikely, but then again I've reproduced a couple of those today already).
So, what's the flaw in this my that leads to this stream of warnings? It'd be nice to say "on preference, do this, then do that, then finish" but I think what I'm doing is the equivalent. Maybe not... thoughts?
Edit: I decided to try doing this ListActivity as a custom Dialog... that was one of the more painful things I've tried to do lately (getApplication() doesn't work and lots of other things seem to go wrong... it may be inexperience to some extent, but dialogs really weren't meant for this either...
Try the following two things:
Dismiss your dialog before calling finish() on its parent activity (PreferenceActivity).
Make sure you are starting your AsyncTask later in the sub-activity's lifecycle. I'm specifically thinking you should launch it in onResume().
My best guess is that the AsyncTask is calling finish() on the sub-activity, before the sub-activity has had a chance to fully start up. Why that would matter? I'm not sure. Something to try though. Good luck!
My app may launch a sub-activity for a specific purpose. When that activity finishes, I get the results in onActivityResult. These results are then processed in the subsequent onResume. This consists of a setContentView and also starting an AsyncTask that puts up a ProgressDialog.
This all works well when initiated the normal way, which is via a user request (i.e., menu selection) after the app is up and running. However, under some conditions I need to do this right as the app is starting up, so I initiate this sequence right from my onCreate. What then happens is that I get fatal ResourceNotFound errors within any o/s call that implicitly calls the layout inflater. I got around this with setContentView by pre-inflating the view in my onCreate method, but the AsyncTask's onPreExecute still fails on ProgressDialog.show() as it "fails to find" Android's own progress_dialog.xml!
Anyone know what's happening here?
I suspect it's something to do with the timing, where this is occurring before the main activity has even had a chance to display its screen. These calls are all being made on the main UI thread, but maybe something hasn't completed within the o/s under these conditions.
As a closeout, the problem turned out to be totally unrelated to what I described in my post. Turns out it was due to blindly using some code that had been posted in some online forum showing how to get and use AssetManager. Trouble is, at the end of the block of code he had put "assMan.close()". Well, this closes the asset manager for the entire activity and resources can no longer be accessed!
It took a while to find it since it was not something that I did via my own understanding.