2009年4月1日 星期三

Testing Android Applications - part 1

Adroid 應用程式的測試方法 - I

圖片引用自 tucowsinc.com

Unit tests and testing are parts of each developer's life and they are important parts of development. Developers usually concentrate on unit tests which help them ensure that future changes will not break the system and let other people do tests and stress tests of applications to reveal bugs, which we as developers simply cannot see.

About This Series of Articles

This is the first article about testing Android applications. We will look at some testing background, discuss basic elements available for you in the SDK and show how to prepare testing environment to run tests. In following articles we will concentrate on describing testing methods in detail.

Short History of Android Testing

Prior to Android SDK 1.0 there was a huge gap in testing of android applications which inspired 3rd-party testing frameworks such as Positron and Electron. With Android SDK 1.0 Google filled the gap with its own testing framework and Positron and Electron frameworks are not needed any more. Filled the gap in testing and filled it well. Let's have a look at what is available there for us. We will concentrate on unit and functional test which are supported with new part of Android SDK.

Unit Testing

There are basically three levels of tests/unit-test you can use in your application:

  1. Unit testing of logic which does not depend on android at all. This is similar to every usual unit testing. Running tests directly from Eclipse requires some configuration changes which we will cover later.
  2. Unit testing of business logic which depends on Android but does not depend on Android application elements and ui. These logic does not require activity to be running with complete context and it can be tested in isolation from ui. These tests usually require something from context (e.g. resources, configuration, logger, some native classes)
  3. Unit/funcional testing of Android application elements. These tests are fully instantiated activities, services, content providers and applications. Via instrumentation it is possible to send keyboard and touch events to the activities and check response in ui. It is possible to test lifecycle of a service and to test databases changes made by content provider.

To be able to test layers of the application in isolation using and fully use prevoius three levels. It is useful to follow these guidelines during design and development phases of your Android application:

  1. Separate business logic from ui logic as much as possible.
  2. Create presenter for complicated activities which models state on the screen and binds to the views according to MVP pattern.
  3. Make clear non expansive contracts between ui layer and business logic.
  4. Return error codes from business logic and let present logic transform them to error messages.
  5. Pass configuraton stored in preferences or in resources in parameters and do not load it in business logic

How to Prepare Testing Environment for Unit Tests

Good starting point for preparing environment for tests which do not depend on Android can be found here. Android runtime in android.jar contains some subset of junit which can not be used out of android environment. If you try to run unit test directly from Eclipse, you get exception like this:

... Internal Error (classFileParser.cpp:2924), pid=11018, tid=3084700560 # Error: ShouldNotReachHere() ...

To be able to run tests from Eclipse you have to replace android.jar by junit.jar

  1. go to "Run Configurations"
  2. search for configuration running your unit test
  3. go to "Classpath" tab
  4. remove "Android Library" from "Bootstrap Entries"
  5. then go to project build path configuration
  6. and add Junit library to your project
  7. you should be able to run junit unit tests

Image on the left shows "Run Configuration" dialog with Android library not deleted and image on the right shows configuration of build path. It is sometimes even possible to unit test classes which depends on classes from android.jar by deleting junit classes in the jar file. But once tested classes requires something from android runtime, you get exceptions.

Setup for Tests Depending on Android

Good starting point is here which shows how to set separate application and test environment but does not show that is possible to have everything working together in one project. You have to setup instrumentation for your project which allows your tests to hook into your application and take control over application ui and pass various events.

  • Add <uses-library android:name="android.test.runner" /> to alement in your manifest file
  • Add test runner definition to your manifest file

where:

  • android:name is name of the test runner class - use android.test.InstrumentationTestRunner as default
  • android:targetPackage is application package you want to instrument
  • android:label is name of the test which appears under Instrumentation in Dev Tools application

How to Run Tests Depending on Android in Eclipse and Emulator

To run tests directly from emulator go to DevTools application in the emulator, select Instrumentation and click on the name of the tests you want to run. Results and lot of debugging output can be found in Logcat console.

How to Run Tests Depending on Android with adb

To run tests from command line enter

adb shell am instrument -w PACKAGE/android.test.InstrumentationTestRunner

where PACKAGE is full name of application package which should be instrumented.

Monkey

Does everything seem to be too complicated for you? Do you wonder if there is simple way how to test Android application? The answer is UI/Application Exerciser Monkey tool available in Android SDK. Monkey is quite fast way for testing of ui. Just be warned that monkey does not like real devices yet. There is bug which causes that monkey is killed by Android whenever it tries to send a notification key to the real T-Mobile G1 device. Now our clever monkey likes to work without bugs with the emulator only.

Next Article...

So far we know what we can test on Android application and we know how to setup and run various kinds of tests. Next time we will look at some tips and hints how to actually write unit and functional tests.

Stay tuned...

註:

這是 Ondrej 的第一篇文章,原先是想幫他在文中譯註關鍵部份。不過我讀了一下,發覺他的用語都還簡單易懂,加上又是技術文章,大家應該有能力,理解他所要傳遞的知識。如果硬在原文中,加上中文譯註,反倒破壞了閱讀者的流暢性。

最後決定,我會在文章的最後,加上關鍵部份的註解。不會逐字翻譯,多是將重點或精神點出。如果你有任何的問題,歡迎在這裡提問,中文發問也可以的。

單元測試 (Unit Testing)

以測試的角度來看,基本上一 個應用程式可分成三層:

  • Logic: 純粹程式邏輯的部份,也就是和 Android 系統以及 UI 無關的部份。
  • Business logic: 和 Android 系統邏輯相關,但和 UI (例如 Activity, View, Dialog, Context, Resources, Logger 都是 UI 相關部份) 無關的部份。
  • UI logic: 當然就是和 UI 息息相關的部份。

要讓你的程式達到 "可測試" 的程度,你必須在寫程式注意一些事情,及遵守下列原則:

  • 程式中要盡量將上面提到的三層,清楚分開
  • 可以利用 MVP (Model, View, Presenter) 設計範式幫你分開,尤其是與 UI 相關及無關的部份,一定要切乾淨。
  • 在 UI 層與 Business logic 層之間的溝通傳遞協定,要盡量簡單清楚。
  • 不要在 Business logic 這層中,直接顯示錯誤訊息。應該要傳回錯誤碼,由 UI 層來顯示。
  • 也不要在 Business logic 層中,直接使用 Android 的 API 去讀取 preferences 設定,或是從 resources 中讀字串值。只有 UI 層,才能使用 Android 的 APIs,讀取到值後,再傳給 Business logic 那一層的。

1 則留言:

阿德 提到...

Hi 恩德雷,
我可以用junt4寫一些unit test,
但是unit test只要去開sd card上檔案的,
都會出現 java.io.FileNotFoundException,
但是實際執行時,確可以正常讀取,請問這是它的限制嗎?
還是我在設定上有什麼地方要注意的?

張貼留言