Archive‎ > ‎Fall 2009‎ > ‎Course Project‎ > ‎Buddy Suite‎ > ‎Tutorial #3‎ > ‎

Using the testing interface

by Laurent

Foreword

Always separate logic from user interface. This is generally good practice for standard applications, but is even more important in mobile applications, typically in Android applications. No matter how tempting it is to incorporate some logic into the UI, don't do it: you will end up paying for it in the long run.

Testing in Android
For a quick guide on how to set up your testing environment, please read last week's tutorial by Cristi.

How JUnit works
(Students already familiar with JUnit may skip that section)
JUnit is a java testing framework. In short, it will allow you to write tests for your code and run them. JUnit's main building block is the TestCase class: all test cases inherit from this class. Each TestCase subclass includes test methods. The convention to follow is to prefix these methods by 'test' (it will also allow JUnit to find your test methods automatically - otherwise they have to be specified manually). A typical JUnit test run is executed as follows:
  1. The test methods are discovered by the framework.
  2. For each test method:
    • The TestCase class is instantiated
    • Its setUp() method is called
    • The test method is called
    • The tearDown() method is called
You might wonder what's the point of such a structure. This guarantees that test methods are independent from one another and that they don't interfere (as a by-product, it also forces the programmer to think of their program as a bunch of independent modules).

Finally, JUnit provides a way to organize TestCases into TestSuites. This allows the programmer to organize their tests hierarchically.

What's different in Android
Not much really! For testing in Android, you should always use android.test.AndroidTestCase instead of junit.framework.TestCase. The difference between the two is that the AndroidTestCase has access to the Context object. As a matter of fact, you could potentially (although we don't advise it) use junit.framework.TestCase for testing the parts of your program that don't rely on the Android framework.

The other difference is that there is no way, unless you want to handle callbacks from the test runner, for you to display the test results on the device (or emulator). However, we don't care about this, the JUnit report one can get from Eclipse is more than enough for our needs.

How to use the test interface
At the beginning of the project, we provided you with a testing interface that will allow programmatic access to your application when implemented. You should make it a point to implement this interface thoroughly as your grade will mostly depend on the results of the tests we will run through that interface.

The interface allows to test functionality without the need for a server and while bypassing the GUI. This is immensely useful as UI testing is very difficult.

We now give a brief tour of the interface.
  • We will use the FriendFinderFactory class to obtain an instance of the class that implements the FriendFinderTestInterface. You are encouraged to keep the current implementation of the newFriendFinder method, but you may change it if you really need to.
  • Please, note that we require you to have the FriendFinderTestInterface in the epfl.sweng.friendfinder package. Do keep it there no matter what! You may of course add import statements at your discretion.
  • We will redirect the messages your program sends to the server using the Destination interface. Typically, one of the classes in the test framework will implement the Destination interface. We will then place a call to the void redirectSendTo(Destination destination); method to perform the redirection.
  • We will simulate the server by calling the void receiveFromServer(String message); method.
  • The other methods can be divided into two sets:
    • Methods that are used to request action (logIn, logOut, setCurrentLocation, etc.), i.e. substitutes for UI events.
    • Methods that are used to get information (getCurrentLocation, getFriendsPosition, etc.).

Example
Below is a rough sketch of a test method that we will use to test your implementation...

Test sketch

class SimpleTest extends AndroidTestCase implements Destination {

    private
FriendFinderTestInterface ffti;

    public void testLogin() {
        ffti = new FriendFinderFactory().newFriendFinder();
        ffti.redirectSendTo(this);
        ffti.ignoreIncomingMessagesFromServer();
        String username = "silviu";
        String password = "silviu";
        String salt = "" + (int) (Math.random() * 1000);
        ffti.receiveFromServer("<hello salt=\"" + salt + "\" ></hello>");
        ffti.logIn(username, password);
    }

    public void receive(String message) {
        // If you want to run this simple example, adapt the three lines below to your code
        String expectedChallenge = /* some calculation */
        Login login = Parser.parse(message);
        String challenge = login.getChallenge();
        assertEquals("Challenge was '" + challenge + "', but should have been '" + expectedChallenge +"'!",
            challenge, expectedChallenge);
        ffti.receiveFromServer("<auth><ack /></auth>");
    }
}


Tips for implementing the interface
  • Check your input
  • Make sure that you don't throw runtime exceptions through the interface

Stuff that may save you some time
As you know, the JUnit testing framework on Android is still pretty new. Here a some of the issues we ran into while implementing our test interface and our testing framework:
  • Apparently, some project rebuilds aren't handled properly: the application on the emulator isn't reinstalled properly, which may result in inconsistent behavior (e.g. you thought you fixed a bug but the tests still fail). The only solution we have to this problem is to exit the emulator, clean and rebuild your project (+ your test project) and rerun the test.
  • Do not put initialization code in the TestCase constructor but in the setUp() method - it will not work!