1

I am building an android application which I want to unit test with JUnit tests, not the Android JUnit tests (because it takes way too long to run the Android tests with the emulator).

Environment:

  • Eclipse
  • Android project
  • Android test project (target package = android project package)
  • JUnit4 / Mockito
  • Running unit tests with JUnit (deliberately not Android JUnit)

As long as I test my own written classes that have no dependencies on Android classes, all goes well. But when I want to write a test for a class that contains e.g. a Log.i() statement or refers to a TextView, then I run into the following error:

java.lang.NoClassDefFoundError: android/text/TextWatcher at java.lang.ClassLoader.defineClass1(Native Method)

Android application manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.cleancode.lifesaver"
    android:versionCode="1"
    android:versionName="1.0" >

Android test project manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.cleancode.lifesaver.test"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="8" />

    <instrumentation
        android:name="android.test.InstrumentationTestRunner"
        android:targetPackage="com.cleancode.lifesaver" />

Class to test:

import android.widget.TextView;

public class CapCharacterTextValidator extends TextValidator {

    public CapCharacterTextValidator(TextView textView) {
        super(textView);
    }

    @Override
    public void validate(TextView textView, String text) {

    }
}

Test class:

import org.junit.Test;
import com.cleancode.lifesaver.utils.CapCharacterTextValidator;

public class CapCharactersTextValidatorTests {

    @Test
    public void validateCharacters() {
        new CapCharacterTextValidator(null);
    }
}

I tried to add the android library in the exported libraries in both the Android application and the Android test application, or one of the two, still nothing gives me a green test. No matter what I try, I keep running into this NoClassDefFoundError.

I read most of the android documentation but they seem to be really fond of the (for me too slow) approach with Android JUnit on the emulator.

Nor do the following Q&A posts help me further:

Any help would be greatly appreciated!

Community
  • 1
  • 1
bas
  • 13,550
  • 20
  • 69
  • 146

3 Answers3

1

To run anything that depends on the Android framework in a unit test, you have to use the Android test runners. Nothing else will work. The framework has to be present.

Joe Malin
  • 8,621
  • 1
  • 23
  • 18
  • Does that mean that I should run my unit tests on the emulator, which is terribly slow? – bas Sep 17 '13 at 05:59
0

Hmm, found an alternative, it might be 'the only way' since I am turning my back to Android JUnit tests all together.

If I create a test project (just a plain Java project with JUnit and Android as libraries instead of the Android test project), then I am allowed to run the unit tests without having the troubles finding the Android libraries in the class path.

If you have the urge to follow the same approach I am, you might also run into the next problem that you can't have Android Log statements in your code, because the Android framework will throw a RuntimeException when you execute e.g. Log.i("tag", "msg").

Since I still appreciate logging, I wrapped the Log class as follows:

import android.util.Log;

public class Logger {

    private static boolean AllowLogging = true;

    public void disableAndroidLogging() {
        AllowLogging = false;
    }

    public static int d(String label, String message) {
        if (AllowLogging) {
            return Log.d(label, message);
        }
        return 0;
    }
}

I disable the Logger in the setup of test classes. That way I dodge the exceptions from the Android framework.

If anybody has better solutions, I am very much open for suggestions.

Many thanks.

bas

bas
  • 13,550
  • 20
  • 69
  • 146
  • You shouldn't disable anything in unit tests, as that means you are testing different code than you would normally run. There are frameworks already designed to do what you want. See my answer... – Dave Sep 17 '13 at 11:36
  • @Dave The only difference is that a message is not written to a log. It would be nice if android allowed `Log` statements while not running on the actual platform, instead they choose to throw an exception. Second, the Android JUnit test framework is absolutely not suitable for the TDD roundtrip. It takes approx 30s - 1 min before the tests start to run. What I have here in my answer works, allows me to work with junit & mockito and runs my tests within milliseconds (as they should). So, if this is the best Android has to give, then I'll stick with this approach. Pros > Cons imho. Thx though! – bas Sep 17 '13 at 13:14
  • Robolectric doesn't use Android JUnit or the emulator. You are *almost* forced to use Maven, which takes a few seconds to build & run tests, but it's not nearly as bad as the alternatives I've seen. But suit yourself. – Dave Sep 17 '13 at 13:40
0

Use Robolectric. It mocks all the Android classes for you.

Dave
  • 4,282
  • 2
  • 19
  • 24
  • Hmm, I just read up on the topic. It might be an alternative, but the part "Robolectric allows a test style that is closer to black box testing, making the tests more effective for refactoring and allowing the tests to focus on the behavior" is not really how I look at TDD. Maybe I will pick this up when I find the need for integration tests, but for now I specifically want to write white box tests in a TDD fashion. Many thanks for the answer and pointers though! – bas Sep 17 '13 at 13:20