Showing posts with label Testing. Show all posts
Showing posts with label Testing. Show all posts

Sunday, January 31, 2010

Unit and Functional Testing in Android

I don't have to tell you that testing is a fundamental part of the product development cycle, although I just did. No matter if you are developing for the desktop, web, or mobile space, you should make sure that the end software system works as required. This blog will go through two flavors of testing on the Android platform: unit and functional.

Unit tests tell the developer that the code runs correctly, while functional tests tell the developer that the code is doing what is supposed to do. To more clearly understand the difference, I will use the analogy mentioned in http://www.ibm.com/developerworks/library/j-test.html. If you think of building a system as being similar to building a house, then think of unit tests as having the building inspector at the construction's site, focusing on making sure that the internal system of the house (foundation, plumbing, etc) works correctly, while functional testing as having the homeowner visiting the house and being interested in how the house looks, if the rooms have the desired size, etc. He assumes that the internal functions of the house work properly.

In unit testing, think of a unit as being the smallest testable part of an application, such as a function/method. The goal is to isolate such parts and make sure they run correctly by being able to test them repeatably. Unit tests are written from the developer's perspective, and as a developer, we have to make sure that we understand the specification and requirements of our software system before writing unit tests. This can be achieved through use cases.

The flow of unit testing is shown below:


Android uses JUnit, and open source framework for writing and running unit tests. Some of the framework's features are:
  • Assertions for testing expected results
  • Test fixtures for sharing common data
  • Test runners for running tests
How does JUnit work? You use assert statements to assert that something is true (assertTrue(expected, actual), assertTrue(condition), etc), that something is false (assertFalse(condition), etc), that something is equal (assertEqual(expected, actual), etc). When an assert fails, the test failed for that particular case, hence your code needs to be fixed (assuming the unit test was written correctly). 

Starting with version 4.x, JUnit takes advantage of Java 5 Annotations:

  • @Test
    • Mark your test cases with the @Test annotation
  • @Before and @After
    • Used for "setup" and "tearDown" methods
    • They run before and after every test case
  • @BeforeClass and @AfterClass
    • Used for class wide "setup" and "tearDown"
    • They run one time, before and after all test cases
    • You write you initialization code (i.e. open database connection) and cleanup code (i.e. close database connection) in here
  • @Ignore
    • Used for test cases you want to ignore (i.e. on methods that are not fully implemented yet, hence are not ready to be ran)
  • Exception Handling
    • Use the "expected" parameter with the @Test annotation for test cases that expect exceptions (i.e. @Test(expected = ArithmeticException.class) ...
Let us look at a Java Example, namely a Calculator application. The class that incorporates the basic functionality of a calculator is shown below:

/**
package edu.fau.csi.junit;

/**
 * Simple class that incorporates the basic functionality of a calculator, providing methods to 
 * add, subtract, multiply, and divide to double numbers.  
 * 
 * @author Mihai Fonoage
 *
 */
public class Calculator {
    /**
     * Left operand of the operation to be performed.
     */
    private double leftOperand;
    /**
     * Right operand of the operation to be performed.
     */
    private double rightOperand;
    
    /**
     * Constructs a Calculator object by initializing the leftOperand and rightOperand 
     * with the given values.
     * 
     * @param leftOperand Left operand.
     * @param rightOperand Right operand.
     */
    public Calculator(double leftOperand, double rightOperand) {
        this.leftOperand = leftOperand;
        this.rightOperand = rightOperand;
    }
    
    /**
     * Adds the leftOperand to the rightOperand.
     * 
     * @return The sum of the two operands. 
     */
    public double add() {
        return leftOperand + rightOperand;
    }
    /**
     * Subtracts the rightOperand from the leftOperand.
     * 
     * @return The subtraction of the two operands. 
     */
    public double subtract() {
        return leftOperand - rightOperand;
    }
    /**
     * Multiply the leftOperand to the rightOperand.
     * 
     * @return The multiplication of the two operands. 
     */
    public double multiply() {
        return leftOperand * rightOperand;
    }
    /**
     * Divides the leftOperand to the rightOperand.
     * 
     * @return The division of the two operands. 
     */
    public double divide() {
        if (rightOperand == 0) {
            throw new ArithmeticException("right operand should not be zero!");
        }
        return leftOperand / rightOperand;
    }
}
I created a new source folder named test and included the following test class (by right-clicking on the project name -> New -> JUnit Test Case, choose "New JUnit 4 test", and as the class under test, search for the above Calculator class):
package edu.fau.csi.junit;

import static org.junit.Assert.assertTrue;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
 * JUnit Test case for the Calculator class.
 * 
 * @author Mihai Fonoage
 *
 */
public class CalculatorTest {
    
    private Calculator calculator;
    
    /**
     * Sets up the test fixture. 
     * (Called before every test case method.)
     */
    @Before
    public void setUp() {
        calculator = new Calculator(6, 4);
    }

    /**
     * Tears down the test fixture. 
     * (Called after every test case method.)
     */
    @After
    public void tearDown() {
        calculator = null;
    }

    /**
     * Test method for {@link edu.fau.csi.junit.Calculator#add()}.
     */
    @Test
    public void testAdd() {
        assertTrue(calculator.add() == 10);
    }

    /**
     * Test method for {@link edu.fau.csi.junit.Calculator#subtract()}.
     */
    @Test
    public void testSubtract() {
        assertTrue(calculator.subtract() == 2);
    }

    /**
     * Test method for {@link edu.fau.csi.junit.Calculator#multiply()}.
     */
    @Test
    public void testMultiply() {        
        assertTrue(calculator.multiply() == 24);
    }
    
    /**
     * Test method for {@link edu.fau.csi.junit.Calculator#divide()}.
     */
    @Test
    public void testDivide() {
        assertTrue(calculator.divide() == 1.5);
    }

    /**
     * Test method for {@link edu.fau.csi.junit.Calculator#divide()}.
     */
    @Test(expected = ArithmeticException.class)
    public void testDivideByZero() {
        Calculator calculator = new Calculator(6, 0);
        calculator.divide();
    }

}
To run everything, right-click on the CalculatorTest class -> Run As -> JUnit Test. All five tests should pass.

Functional tests are written from the user's perspective. They confirm that the system does what the user expected it to do, based on the functional requirements of the system, hence it focuses on the behavior that users are interested in. The tester interacts with the system to determine if the behavior is correct.

In Android, functional testing is possible through the android.test.* package(s). We are going to use the same Calculator example, adapted for Android. When you create the Calculator Android project in Eclipse, choose also co create a Test Project for it. You will end up with two project, the Calculator project and the CalculatorTest project. The layout of the Calculator project is described below:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/linear" 
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical">
    
    <TableLayout android:id="@+id/table" 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" 
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" 
        android:stretchColumns="1">
        <TableRow>
            <TextView android:id="@+id/leftOperand_label" 
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" 
                android:padding="3dip"
                android:textStyle="bold" 
                android:text="Left Operand">
            </TextView>
            <EditText android:id="@+id/leftOperand" 
                android:padding="3dip"
                android:numeric="decimal"
                android:singleLine="true"
                android:scrollHorizontally="true" 
                android:nextFocusDown="@+id/rightOperand">
            </EditText>
        </TableRow>
        <TableRow>
            <TextView android:id="@+id/rightOperand_label" 
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" 
                android:padding="3dip"
                android:textStyle="bold" 
                android:text="Right Operand">
            </TextView>
            <EditText android:id="@+id/rightOperand" 
                android:padding="3dip"
                android:singleLine="true"
                android:scrollHorizontally="true"
                android:nextFocusDown="@+id/plus">
            </EditText>
        </TableRow>
    </TableLayout>
    
    <LinearLayout android:id="@+id/buttons_linear" 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        
        <Button android:id="@+id/plus" 
            android:layout_width="0px"
            android:layout_height="wrap_content" 
            android:layout_weight="1"
            android:padding="6dip"
            android:text="+" 
            android:layout_gravity="center_horizontal" 
            android:nextFocusRight="@+id/minus">
        </Button>
        
        <Button android:id="@+id/minus" 
            android:layout_width="0px"
            android:layout_height="wrap_content" 
            android:layout_weight="1"
            android:padding="6dip"
            android:text="-" 
            android:layout_gravity="center_horizontal" 
            android:nextFocusRight="@+id/multiply">
        </Button>
        
        <Button android:id="@+id/multiply" 
            android:layout_width="0px"
            android:layout_height="wrap_content" 
            android:layout_weight="1"
            android:padding="6dip"
            android:text="*" 
            android:layout_gravity="center_horizontal" 
            android:nextFocusRight="@+id/divide">
        </Button>
        
        <Button android:id="@+id/divide" 
            android:layout_width="0px"
            android:layout_height="wrap_content" 
            android:layout_weight="1"
            android:padding="6dip"
            android:text="/" 
            android:layout_gravity="center_horizontal" 
            android:nextFocusRight="@+id/divide">
        </Button>
        
        <Button android:id="@+id/clear" 
            android:layout_width="0px"
            android:layout_height="wrap_content" 
            android:layout_weight="1"
            android:padding="6dip"
            android:text="C" 
            android:layout_gravity="center_horizontal" 
            android:nextFocusRight="@+id/divide">
        </Button>
        
    </LinearLayout>
    
    <TextView android:id="@+id/result" 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" 
        android:padding="20dip"
        android:textStyle="bold" 
        android:gravity="right"
        android:text="Result">
    </TextView>
    
</LinearLayout>

It is a simple layout, with two initial text fields for the left and right operand, five buttons, each for one operation, plus a clear button, and one text field that will hold the result of the calculation. As a side note, I have put the text for each of these elements inside the layout xml file to save some time; you should put them inside the strings.xml file instead.

The CalculatorActivity class is described next:

package edu.fau.csi.calculator;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class CalculatorActivity extends Activity implements OnClickListener {

    private EditText leftOperand;

    private EditText rightOperand;

    private TextView result;
    
    private Calculator calculator;
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.calculator);
                        
        leftOperand = (EditText)findViewById(R.id.leftOperand);
        rightOperand = (EditText)findViewById(R.id.rightOperand);
        result = (TextView)findViewById(R.id.result);

        Button plus = (Button)findViewById(R.id.plus);
        plus.setOnClickListener(this);
        Button minus = (Button)findViewById(R.id.minus);
        minus.setOnClickListener(this);
        Button multiply = (Button)findViewById(R.id.multiply);
        multiply.setOnClickListener(this);
        Button divide = (Button)findViewById(R.id.divide);
        divide.setOnClickListener(this);
        Button clear = (Button)findViewById(R.id.clear);
        clear.setOnClickListener(this);

    }

    @Override
    public void onClick(View view) {
        double leftOp = Double.parseDouble(leftOperand.getText().toString());
        double rightOp = Double.parseDouble(rightOperand.getText().toString());
        calculator = new Calculator(leftOp, rightOp);
        
        if (view.getId() == R.id.plus) {
            result.setText("" + calculator.add());
        }
        else if (view.getId() == R.id.minus) {
            result.setText("" + calculator.add());
        }
        else if (view.getId() == R.id.multiply) {
            result.setText("" + calculator.multiply());
        }
        else if (view.getId() == R.id.divide) {
            result.setText("" + calculator.divide());
        }
        else if (view.getId() == R.id.clear) {
            leftOperand.setText("");
            rightOperand.setText("");
            result.setText("");
            leftOperand.requestFocus();
        }
    }
}

The Calculator class that actually does the calculations is exactly the same as the one from the Java example.

When you run this example, you will get the following screen:

The unit testing part is the same as the one mentioned in the unit testing section of this blog. You can still test your business logic without needing to interact with the application through its user interface. Actually, Calculator.java and CalculatorTest.java were just copied from the Java example to the Android without making any modifications.

Functional testing for this example involves the process of entering numbers in the two text fields, pressing one of the operations button, and checking the result to make sure it is the expected one. Instead of having the developer/user/tester do this manually, we can automate the task by sending key events through instrumentation, all from the CalculatorActivityTest class from the CalculatorTest application:
package edu.fau.csi.calculator.test;

import android.test.ActivityInstrumentationTestCase2;
import android.view.KeyEvent;
import android.widget.TextView;
import edu.fau.csi.calculator.CalculatorActivity;


public class CalculatorActivityTest extends ActivityInstrumentationTestCase2<CalculatorActivity>{

    private TextView result;

    private CalculatorActivity calculatorInstance;

    public CalculatorActivityTest() {
        super("edu.fau.csi.calculator", CalculatorActivity.class);
    }

    /* (non-Javadoc)
     * @see android.test.ActivityInstrumentationTestCase2#setUp()
     */
    @Override
    protected void setUp() throws Exception {
        // TODO Auto-generated method stub
        super.setUp();
        calculatorInstance = (CalculatorActivity) getActivity();
        result = (TextView)calculatorInstance.findViewById(R.id.result);
    
    }

    /**
     * Test the addition operation of the CalculatorActivity
     * 
     * @throws Throwable
     */
    public void testAdd() throws Throwable {
        
        //First field value
        sendKeys( KeyEvent.KEYCODE_3 );
        sendKeys( KeyEvent.KEYCODE_PERIOD );
        sendKeys( KeyEvent.KEYCODE_5 );
        
        //Move to the second field
        sendKeys( KeyEvent.KEYCODE_DPAD_DOWN );
        sendKeys( KeyEvent.KEYCODE_2 );
        sendKeys( KeyEvent.KEYCODE_PERIOD );
        sendKeys( KeyEvent.KEYCODE_1 );

        //Move to the '+' button
        sendKeys( KeyEvent.KEYCODE_DPAD_DOWN );
        sendKeys( KeyEvent.KEYCODE_DPAD_CENTER );

        //Wait for the activity to finish all of its processing.
        getInstrumentation().waitForIdleSync();
        
        //Use assertion to make sure the value is correct
        assertTrue(result.getText().toString().equals("5.6"));
        
    } 
}

The above activity test class inherits from the ActivityInstrumentationTestCase2 that provides functional testing of a single activity, namely our CalculatorActivity. The above code only tests the add method, but similar implementations can be done for all other three calculations.

The Android manifest file for the CalculatorTest project is described next:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="edu.fau.csi.calculator.test"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">

    <uses-library android:name="android.test.runner" />
    </application>
    <uses-sdk android:minSdkVersion="4" />
    <instrumentation android:targetPackage="edu.fau.csi.calculator" 
        android:name="android.test.InstrumentationTestRunner" />
</manifest>

When the activity is ran for the first time, you have to create a new Android JUnit Test configuration. There are two possibilities, both shown below:

 or


When you run this, the Calculator application will start in the emulator, you will see the two text fields being populated with the values 3.5 and 2.1, the "+" button being pressed, and the result field populated with the result of the operation, namely 5.6.

I hope you find this helpful!

Friday, April 25, 2008

Testing Mobile Applications

Mobile developers will tell you that probably the hardest part of the entire mobile application development process is testing. You would need access to hundreds of mobile devices, test on each and every one, and make sure that the features you implemented in your application work properly on each and every test device. Sure we have emulator, but all they provide is an emulated environment of the phone. Sure you should start with an emulator when testing the application, but eventually, to see how it works on a real device, you need to push further. The question that unfolds is what can you do if your company does not have access to hundreds and hundreds of devices. Here are a few possibilities:

Motorola offers a Handset Loaner Program through which you can test your mobile application on Motorola handsets. The kit that you will receive will consist of the Handset, Charger, Battery, and a USB Cable. The cost of the kit for a 30 day loan varies between $35 and $70. There are not a lot of devices available as far as I can tell. If you loose or damage the device, the fee is $1000 for most of the devices. A lot isn't it? Make sure you do not damage or loose the device.

Nokia offers the Remote Device Access program free of charge for all Nokia Forum members. It is a service that allows you to remotely test your mobile application on Symbian OS-based Nokia devices. "The main features of the service are remote controlling a device, installing and running applications, transferring files, and analyzing log files in real-time."

Sony Ericsson Virtual Lab service allows for remote testing of your mobile applications on selected 2007 Sony Ericsson mobile phones that are connected to a network 24/7. The cost varies between $18 to $20 an hour. The platforms supported are Java ME and UIQ 3.

Probably the most well known service is DeviceAnywhere, and this is because of the high number of devices/manufacturers/operators supported. It offers remote access to almost one thousand mobile devices from more than 25 manufacturers that run on more than 20 networks from places like Japan, UK, US, France, etc. Individual device packages are $100 a month each (and you can choose from over 20 packages); the hourly rate various between $13 and $16 and the hours can be used across all device packages. The unused hours do not roll over to the next month, since unused hours expire at the end of each month. You do get 3 hours of free trial to try the service.

DotMobi Virtual Developer Lab, powered by DeviceAnywhere, offers you 5 free hours to test your mobile application.

PACA Mobile Center offers access to more than 850 handsets. As far as I understood, you need to go to one of their centers (located in France) to get access to the pool of devices. The daily access cost is 500 euros, with the possibility of a 10 day plan starting from 3000 euros, and a yearly plan of 12000 euros. For start up companies, the fees are usually half off the regular price mentioned.

Mob4Fire implements the concept of crowed sourced mobile application testing. How it works is easy and straightforward: "The developer chooses a mix of handsets, network and OS's to test the software on, gets it tested by the crowed, gets feedback and pays the tester." There is an online rating system where testers are rated based on the quality and timeliness of the response. Developers are also tested based on how clearly they outlined the job testers need to do.

I will keep adding to this blog if I find new information. If you want to participate, you are very welcome, so please leave comments.

Thursday, April 17, 2008

U.S. Game Industry Salary

According to a survey by GameDaily, in the U.S., the average salary in the gaming industry for 2007 was $73,600. Breaking down by groups we have:

  • The business and marketing side have an average salary of $101,848
  • Programmers have an average of $83,383
  • Game producers earned on average $78,716
  • Sound designers had an average of $73,409
  • Artists (that deal with art and animation) earned on average $66,594
  • Quality assurance people had an average of $39,063.
In my opinion, an artist should earn more than a sound designer since for me in a game, the overall animation is more important than the sound. The salary for QA people is much to low. They make sure that everything works together like it's supposed to. If a bug is discovered in the late stages of the product, or even after deployment, the negative impact is even higher. Hence it is the QA guys that have the daunting task of discovering every little bug that hides in every little corner of your product. Related to this, I found an interesting (short and to the point) blog.