Thursday, November 06, 2008

Android: Testing on the Android platform - Unit tests

This is the first installment of a series of articles about testing on the Android platform. Things have changed with the release of the version 1.0 of the SDK and will change in the future for sure, as there are still some lose ends.
Electron and Positron frameworks are not valid anymore in this new SDK, so let's back to base and start analyzing the possibilities featured in the Android platform as it is.


As with previous version of articles covering testing for different platforms we will be using our simple Temperature Converter (also analyzed in Test Driven Development and GUI Testing) example.

For a Test Driven Development approach you should
  1. create your Android project
  2. add the tests source folder (New -> Source Folder -> Folder name: tests/src)
  3. in the newly created folder add a Java Package. Its name should be the same as the project package with .tests appended
  4. in the 'tests' package create a new JUnit Test Case using JUnit 3 (New -> JUnit Test Case). Name it TemperatureConverterTest, leave class under test blank
  5. Copy this code into TemperatureConverterTest.java 
package com.codtech.android.temperatureconverter.tests;

      import junit.framework.TestCase;

          import java.util.HashMap;
            import java.util.Map;

                import com.codtech.android.temperatureconverter.TemperatureConverter;

                    /**
                       * @author diego
                         *
                           */
                            public class TemperatureConverterTest extends TestCase {

                                private static final Map conversionTable = new HashMap();

                                    static {
                                      // initialize (c, f) pairs
                                        conversionTable.put(0, 32);
                                          conversionTable.put(100, 212);
                                            conversionTable.put(-1, 30);
                                              conversionTable.put(-100, -148); conversionTable.put(32, 90);
                                                conversionTable.put(-40, -40);
                                                  conversionTable.put(-273, -459);
                                                    }

                                                        /**
                                                          * @param name
                                                            */
                                                              public TemperatureConverterTest(String name) { super(name);
                                                                }


                                                                      /**
                                                                        * Test method for {@link com.codtech.android.temperatureconverter.TemperatureConverter#celsiusToFahrenheit(int)}.
                                                                          */
                                                                            public void testCelsiusToFahrenheit() {
                                                                              for (int c: conversionTable.keySet()) {
                                                                                int f = conversionTable.get(c);
                                                                                  String msg = "" + c + "C -> " + f + "F"; int r = TemperatureConverter.celsiusToFahrenheit(c);
                                                                                    assertEquals(msg, f, r);
                                                                                      }
                                                                                        }
                                                                                          }

                                                                                          1. Accept Eclipse hints to create TemperatureConverter class and celsiusToFahrenheit method
                                                                                          2. Run -> Run As -> Run configurations and create a new JUnit configuration and select the previously created Test class
                                                                                          3. Run the tests and instead of the expected test results you'll get
                                                                                            # An unexpected error has been detected by Java Runtime Environment:
                                                                                              #
                                                                                                #  Internal Error (classFileParser.cpp:2924), pid=5364, tid=6644
                                                                                                  #  Error: ShouldNotReachHere
                                                                                                  1. That's because we are using Android's JUnit stub implementation. Go to Run -> Run As -> Run configurations again and in the recently created JUnit configuration Classpath's Bootstrap Entries remove Android Library
                                                                                                  2. Then Add Library, using Advanced... button, and add JRE System Library and JUnit 3
                                                                                                  3. Apply and Run
                                                                                                  4. And now our tests run, failing as expected. We haven't created the conversion method anyway





                                                                                                  So far so good, our tests are running. But as you may have discovered already, mainly when we changed the libraries, those tests are not Android platform tests so they are only useful as part of our Test Driven Development strategy and we must have always in mind that real Android implementation of some functionality may vary.

                                                                                                  1. Let's finish our TemperatureConverter class
                                                                                                  2. package com.codtech.android.temperatureconverter;

                                                                                                    /**
                                                                                                       * @author diego
                                                                                                         *
                                                                                                           */
                                                                                                            package com.codtech.android.temperatureconverter;
                                                                                                              public class TemperatureConverter {
                                                                                                                public static final int ABSOLUTE_ZERO_C = -273;

                                                                                                                    public static int celsiusToFahrenheit(int c) {
                                                                                                                      if (c < ABSOLUTE_ZERO_C ) {
                                                                                                                      throw new RuntimeException("Invalid temperature: " + c + " below absolute zero");
                                                                                                                      }
                                                                                                                      return (int)Math.round(c * 1.8 + 32);
                                                                                                                      }
                                                                                                                      }

                                                                                                                      Run the test again and it will success. A Test Suite can allso be added, however in this case as we have only one test it's not necessary.

                                                                                                                      Stay tuned. Next article will explore Android Instrumentation to run tests on the real platform.


                                                                                                                      16 comments:

                                                                                                                      Erds said...

                                                                                                                      I am relatively new to Android development as well as Eclipse. I typically approach Test Driven Development from an interface based separation perspective to allow for better encapsulation and therefore testability. Once I have a presenter speaking to an interface, I mock out the implementer of the interface. I have been attempting to establish a MockContext, but have been unsuccessful. Any help would be appreciated.

                                                                                                                      Diego Torres Milano said...

                                                                                                                      Thanks for you comment.
                                                                                                                      I'm planning a post with an example using Mock classes soon.

                                                                                                                      Anonymous said...

                                                                                                                      Could you give me a hint on where to start :)....

                                                                                                                      Pankaj Bisaria said...

                                                                                                                      Hello Diego,

                                                                                                                      Thanks for this nice article. Your article helped me a lot. I was searching a lot but find the solution in only your's article.

                                                                                                                      Thanks once again for your help.

                                                                                                                      Diego Torres Milano said...

                                                                                                                      Thanks for your comments.
                                                                                                                      I'm glad to hear that it was helpful.

                                                                                                                      Rick said...

                                                                                                                      Great article. TDD on the android is very limited and there isn't a lot out there to meet the growing demand.

                                                                                                                      I've been plagued for a couple weeks on getting a solid framework up, finally I get everything up and BAM - Java VM error!

                                                                                                                      This post solved that problem!
                                                                                                                      Thank you for taking the time to post this tutorial, really appreciated.

                                                                                                                      stanb said...

                                                                                                                      I'm not sure why is Positron not a valid option for functional testing in 1.0 even 1.5 Android SDK app? I'm using it. Could you explain, what you meant?

                                                                                                                      Diego Torres Milano said...

                                                                                                                      Thanks for your comment.

                                                                                                                      By the time I wrote this post some SDK versions were not supported by Positron.
                                                                                                                      That's what I meant.

                                                                                                                      Hermit_Insane said...

                                                                                                                      Hi stanb,

                                                                                                                      You said you are using positron framework on SDK 1.5.
                                                                                                                      I have a problem using the same framework.
                                                                                                                      I tried using the notepad example from :
                                                                                                                      http://code.google.com/p/autoandroid/source/browse/#svn/trunk/samples/notepad

                                                                                                                      I am able to install the app but when I run the stories as junit tests from the eclipse, I get the following error :
                                                                                                                      #
                                                                                                                      # A fatal error has been detected by the Java Runtime Environment:
                                                                                                                      #
                                                                                                                      # Internal Error (classFileParser.cpp:3075), pid=1605, tid=3076537232
                                                                                                                      # Error: ShouldNotReachHere()
                                                                                                                      #
                                                                                                                      # JRE version: 6.0_14-b08
                                                                                                                      # Java VM: Java HotSpot(TM) Client VM (14.0-b16 mixed mode linux-x86 )
                                                                                                                      # An error report file with more information is saved as:
                                                                                                                      # /home/rishi/Rapid/notepad/hs_err_pid1605.log
                                                                                                                      #
                                                                                                                      # If you would like to submit a bug report, please visit:
                                                                                                                      # http://java.sun.com/webapps/bugreport/crash.jsp


                                                                                                                      Please suggest if I am missing something.

                                                                                                                      Thanks in Advance,
                                                                                                                      Rishi

                                                                                                                      stanb said...

                                                                                                                      Rishi, this is not a big issue. The reason is, the stories need to be executed on the desktop rather than on an Android application. To change this, instead of selecting "Run as JUnit", select from the same menu "Configurations". Then, go to libraries or ClassPath (don't remember) and remove "Android Runner" (it's called not exactly this, but similar). Then select the same header that the "Android Runner" was under and click Advance, and Add Library and select Standard JRE or something like this. Click OK to confirm etc. And then, repeat the same to add "JUnit". Once this is done, you can run "Run as Junit" like you tried before and it will run fine.
                                                                                                                      Once, I get home (I'm at work right now), I can email you more precise instructions. I have them on my home laptop. Let me know if you need them.

                                                                                                                      Unknown said...

                                                                                                                      I just had this problem today. I managed to fix it with stanb procedure.
                                                                                                                      To be a bit more precise on Eclipse 3.5.1:
                                                                                                                      01_Right click on the project -> run -> run configuration
                                                                                                                      02_Select your Junit project
                                                                                                                      03_Go to the classpath tab
                                                                                                                      04_remove the Android framework entry
                                                                                                                      05_select bootstrap entries
                                                                                                                      06_click on advanced
                                                                                                                      07_select Add Library
                                                                                                                      08_Ok
                                                                                                                      09_Chose "JRE System Library"
                                                                                                                      10_Next
                                                                                                                      11_finish
                                                                                                                      12_You need to also add the JUnit library so follow the steps 05 to 11 and select the "Junit" instead of "JRE System Library"
                                                                                                                      13_You can now run your project as Junit.

                                                                                                                      Works fine now!

                                                                                                                      Diego Torres Milano said...

                                                                                                                      Thanks for your comment.
                                                                                                                      Just a quick note, remember that this approach is only valid for unit testing of non android related classes.
                                                                                                                      If you are testing classes depending on android ones you should use Run As -> Android Junit Test.

                                                                                                                      drozzy said...

                                                                                                                      Thanks, this helped.

                                                                                                                      wiki said...

                                                                                                                      Hello..
                                                                                                                      Can you suggest if I wanted to run these JUNit test via maven ?
                                                                                                                      I was able to write the test cases with the article you have provided.
                                                                                                                      But then I have the requirement where I want to run these test cases via mavan android plugin.Right now its failing.

                                                                                                                      Anonymous said...

                                                                                                                      This is good resourceful information for lots of android peoples. I am also getting good knowledge from your blog and your explanation is easy to understand. so thanks for providing this good knowledge.

                                                                                                                      Android app developer

                                                                                                                      shreyas sheshadri said...

                                                                                                                      Hey Stanb and Diego I'm experiencing the same problem if i run the test project from eclipse i get the following error :
                                                                                                                      #
                                                                                                                      # A fatal error has been detected by the Java Runtime Environment:
                                                                                                                      #
                                                                                                                      # Internal Error (classFileParser.cpp:3174), pid=2164, tid=6344
                                                                                                                      # Error: ShouldNotReachHere()
                                                                                                                      #
                                                                                                                      # JRE version: 6.0_20-b02
                                                                                                                      # Java VM: Java HotSpot(TM) Client VM (16.3-b01 mixed mode windows-x86 )
                                                                                                                      # An error report file with more information is saved as:
                                                                                                                      # C:\Users\Althea\workspace\aphidmobileTest\hs_err_pid2164.log
                                                                                                                      #
                                                                                                                      # If you would like to submit a bug report, please visit:
                                                                                                                      # http://java.sun.com/webapps/bugreport/crash.jsp
                                                                                                                      #
                                                                                                                      Please help me out