user1987343
user1987343

Reputation: 93

Robolectric & testing startActivityForResult

I have troubles with my Robolectric unit test.

I can assert without problem that a click started a new activity when the listener uses the method startActivity( Intent )

But it seems Robolectric has trouble when a new activity is started with the method startActivityForResult(Intent, int) : putting some breaks in the code made me figure out that the activity wasn't started ( and just changing for method startActivity( Intent ) made the assert pass).

Is that normal ? It's a pity since the first activity of my app uses startActivityForResult(Intent, int).

Did someone manage to make tests with this way of launching activities ?

Thanks for your help ..

Upvotes: 3

Views: 6071

Answers (2)

MattDavis
MattDavis

Reputation: 5178

The short Answer to your question is that, due to the way Robolectric converts Android classes to code that executes in the JVM, a lot of their functionality doesn't behave as you'd expect. Many system callbacks won't execute, and you'll have to rely on what Robolectric provides in their Shadow implementation of classes. (See the link provided by @Steven_BDawg).

The long answer: It may be possible to implement this whole flow in one big test, but it's not what the framework is designed for.

Robolectric and Unit Testing in general aren't meant to be used in the way that you describe. The Unit Testing page on wikipedia states that one can view a unit as the smallest testable part of an application. A unit testing suite should contain many lightweight tests, where each test isolates a bit of functionality in your app and ensure it's working properly.

Consider a basic Application that contains two Activities, A and B. Activity A displays some information about a topic, and Activity B allows the user to select which topic to show in A. When the user moves from Activity A to Activity B, B gets called with startActivityForResult() and should return to A with the selected topic.

Now say we want to Unit Test this flow of A getting the result from B and displaying the data. We can break this up into two tests:

  1. Activity Under Test - Activity A. In our test, we'll create a new instance of Activity A. In the Robolectric Test, we create the Intent that we expect B to return to A, and call the shadow method receiveResult() for A, filling out the arguments with a result code of OK and this Intent. After receiveResult(), run your assertions. You now know that Activity A handles the result properly!
  2. Activity Under Test - Activity B. In our test, we'll create a new instance of Activity B, setting it up as if it were started for result from Activity A. In the Robolectric Test, we'll perform all actions needed to select the data, create the intent we'll send back, then run assertions on the intent to ensure it was created correctly.

This is a very simple example. These two steps could probably be broken out into many more tests, as, again, each unit test should only be testing the smallest unit of functionality that your app can be broken into. The example is mainly to help you start thinking in a unit testing kind of way. I've found that as my understand of unit testing deepens, the way I write code has changed. I try to avoid writing methods and classes in such a way that they do too much work and cannot be properly unit tested. As a rule of thumb, code that's easy to unit test performs very specific operations which are readily apparent when reading the code for the first time.

Finally, if you want to take this a step further, mocking frameworks can greatly aid your ability to Unit Test. Mockito is a mocking framework I've had success with in the past. The purpose of a mocking framework is to create stub Objects whose behavior you tightly control. Mockito (or any other Mocking Framework), will allow you to define an object that extends from any type you need and only implement the methods you need. You'll be able to directly control the response to any of these method invocations. This aids Unit Testing because the only real object that you'll need is the Object Under Test; by Mocking all other objects, you'll have a better sense of whether or not the Object Under Test is behaving properly because all other behavior is explicitly defined by you, the tester. (And yes, this does lead to lots of extra code, but such is the life of a good unit tester. However, as previously stated, as you get more comfortable with unit testing, you may find yourself writing methods that require less mocking and are more conducive to writing tests. Some coders will even write their unit tests BEFORE they code, in order to keep their code tight and focused on a single purpose)

Hope this helps!

Upvotes: 5

Steven_BDawg
Steven_BDawg

Reputation: 806

I did some Google'ing for you. I don't know if this will definitely help you or not, but I think it is a good start! Roboelectric: Testing startActivityForResult() and onActivityResult()

Upvotes: 3

Related Questions