kingston
kingston

Reputation: 11419

unit test android

Unit testing android application is often more difficult than expected because there are classes that do not have a public constructor (like NotificationManager) and classes that have methods that is not possible to override like Context.getText.

For NotificationManager I used a delegator. I would do the same for the context but that means that in all the classes that use the context (many) I need to use my own context that can't even be derived from Context. Then whenever I need to pass the context to an Android API I need to get the real context from within my wrapper.

Is this correct? Is there another way to do it? What is the rationale of having those methods declared as final? Is there someone that has really written unit tests for big applications for Android?

EDIT

I found the reason why the getText method is defined as final: it's a template method. So it is enough to override the called non final methods

I also found this that works but it is a bit weird

Upvotes: 2

Views: 2033

Answers (3)

yorkw
yorkw

Reputation: 41126

Question is a little bit vague so I am going to write a long answer.

there are classes that do not have a public constructor (like NotificationManager)

Because there are some system-wide resources/components (for instance NotificationManager) which Android doesn't like application to construct/instantiate on demand, instead, these resources are centralized and managed by system and application should always use system provided API to acquire them.

and classes that have methods that is not possible to override like Context.getText

Generally in Java, method marked as final means they are protected by API and API developer doesn't want the default behaviour to be overridden by application developer. In particular for this method Context.getText(), it actually use Template method pattern, check out herschel's comment below and the source code for more details.

Then the question is how do we properly test our code written by using these android context based resources/components?

The answer given by Android SDK is Test Project (via InstrumentationTestRunner). Generally, when using InstrumentationTestRunner, behind the sense, system will install both test.apk (build from Test Project) and app.apk (build from Application Project), and use test.apk manipulate app.apk for testing. This is happened Regardless of whether you use AndroidTestCase API (referred as JUnit in diagram below) or InstrumentationTestCase API (referred as Instrumentation in diagram below) The corresponding testable android context based resoures/components (i.e. Activity, NotificationManager or TextView) are all acquired from the real running app.apk instance. In case you create/use your own MockContext in Test Project, you can see from the diagram that mock object is injected into the running application by InstrumentationTestRunner.

enter image description here

The drawback of using instrumentation test is about efficiency, it goes into the complete running life cycle (start emulator -> install and start both test.apk and app.apk and fire InstrumentationTestRunner) even though you just need test a single method from a single class (as you mentioned in comment). I agree with Ollie, it is a hostile design.

This is where Robolectric come in and play, quouting from their website: Robolectric is a unit test framework that de-fangs the Android SDK jar so you can test-drive the development of your Android app. Tests run inside the JVM on your workstation in seconds.

From my own experience, I have an application consists of around 10 activities and several Service with some other custom views/widgets doing mainly http communication with remote server, and use both instrumentation test (via Test Project) and Robolectic testing Android Context based code. I can pretty much test almost every single class/public method of my application as long as time is allowed.

Upvotes: 2

Justin Breitfeller
Justin Breitfeller

Reputation: 13801

The *TestCase classes that Android provides for you should be able to solve the testing issues you are encountering. In most of these instances it will spawn a context for you that you can override if you wish. I would suggest starting there, or asking more specific questions about how you would test one thing or another.

Upvotes: 0

Ollie C
Ollie C

Reputation: 28519

Android is pretty hostile to testing in many respects.

You might want to take a look at Roboelectric http://pivotal.github.com/robolectric/

I assume you've read these:

http://developer.android.com/guide/topics/testing/index.html

http://developer.android.com/resources/tutorials/testing/helloandroid_test.html

Upvotes: 0

Related Questions