How to detect whether android app is running UI test with Espresso-ThrowExceptions

Exception or error:

I am writing some Espresso tests for Android. I am running in the the following problem:

In order for a certain test case to run properly, I need to disable some features in the app. Therefore, in my app, I need to detect whether I am running Espresso test so that I can disable it. However, I don’t want to use BuildConfig.DEBUG to because I don’t want those features to be disabled in a debug build. Also, I would like to avoid creating a new buildConfig to avoid too many build variants to be created (we already have a lot of flavors defined).

I was looking for a way to define buildConfigField for test but I couldn’t find any reference on Google.

How to solve:

Combined with CommonsWare’s answer. Here is my solution:

I defined an AtomicBoolean variable and a function to check whether it’s running test:

private AtomicBoolean isRunningTest;

public synchronized boolean isRunningTest () {
    if (null == isRunningTest) {
        boolean istest;

        try {
            Class.forName ("myApp.package.name.test.class.name");
            istest = true;
        } catch (ClassNotFoundException e) {
            istest = false;
        }

        isRunningTest = new AtomicBoolean (istest);
    }

    return isRunningTest.get ();
}

This avoids doing the try-catch check every time you need to check the value and it only runs the check the first time you call this function.

###

Combining Commonsware comment + Comtaler’s solution here’s a way to do it for any test class using the Espresso framework.

public static synchronized boolean isRunningTest () {
        if (null == isRunningTest) {
            boolean istest;

            try {
                Class.forName ("android.support.test.espresso.Espresso");
                istest = true;
            } catch (ClassNotFoundException e) {
                istest = false;
            }

            isRunningTest = new AtomicBoolean (istest);
        }

        return isRunningTest.get();
    }

###

Building on the answers above the following Kotlin code is equivalent:

val isRunningTest : Boolean by lazy {
    try {
        Class.forName("android.support.test.espresso.Espresso")
        true
    } catch (e: ClassNotFoundException) {
        false
    }
}

You can then check the value of the property:

if (isRunningTest) {
  // Espresso only code
}

###

How about a flag in BuildConfig class?

android {
    defaultConfig {
        // No automatic import :(
        buildConfigField "java.util.concurrent.atomic.AtomicBoolean", "IS_TESTING", "new java.util.concurrent.atomic.AtomicBoolean(false)"
    }
}

Add this somewhere in your test classes.

static {
    BuildConfig.IS_TESTING.set(true);
}

###

i prefer not to use reflection which is slow on android. Most of us have dagger2 set up for dependency injection. I have a test component set up for testing. Here is a brief way you can get the application mode (testing or normal):

create a enum:

public enum ApplicationMode {
    NORMAL,TESTING;
}

and a normal AppModule:

@Module
public class AppModule {

    @Provides
    public ApplicationMode provideApplicationMode(){
        return ApplicationMode.NORMAL;
    }
}

create a test runner like me:

public class PomeloTestRunner extends AndroidJUnitRunner {

    @Override
    public Application newApplication(ClassLoader cl, String className, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
            return super.newApplication(cl, MyTestApplication.class.getName(), context);
    }
}

dont forget to declare it in gradle like this:

defaultConfig {
testInstrumentationRunner "com.mobile.pomelo.base.PomeloTestRunner"
}

Now create a subclass of the AppModule with override method that looks exactly like this and do not mark it as a module above the class definition :

public class TestAppModule extends AppModule{

    public TestAppModule(Application application) {
        super(application);
    }

    @Override
    public ApplicationMode provideApplicationMode(){
        return ApplicationMode.TESTING; //notice we are testing here
    }
}

now in your MyTestApplication class that you declared in custom test runner have the following declared:

public class PomeloTestApplication extends PomeloApplication {

    @Singleton
    @Component(modules = {AppModule.class})
    public interface TestAppComponent extends AppComponent {
        }

    @Override
    protected AppComponent initDagger(Application application) {
        return DaggerPomeloTestApplication_TestAppComponent.builder()
                .appModule(new TestAppModule(application)) //notice we pass in our Test appModule here that we subclassed which has a ApplicationMode set to testing
                .build();
    }
}

Now to use it simply inject it in production code wherever like this:

@Inject
    ApplicationMode appMode;

so when your running espresso tests it will be testing enum but when in production code it will be normal enum.

ps not necessary but if you need to see how my production dagger builds the graph its like this and declared in application subclass:

 protected AppComponent initDagger(Application application) {
        return DaggerAppComponent.builder()
                .appModule(new AppModule(application))
                .build();
    }

###

I’ll create two files like below

src/main/…/Injection.java

src/androidTest/…/Injection.java

And in Injection.java I’ll use different implementation, or just a static variable int it.

Since androidTest is the source set, not a part of build type, I think what you want to do is hard.

###

If you are using JitPack with kotlin. You need to change Espresso’s package name .

val isRunningTest : Boolean by lazy {
    try {
        Class.forName("androidx.test.espresso.Espresso")
        true
    } catch (e: ClassNotFoundException) {
        false
    }
}

For checking

if (isRunningTest) {
  // Espresso only code
}

###

You can use SharedPreferences for this.

Set debug mode:

boolean isDebug = true;

SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putInt("DEBUG_MODE", isDebug);
editor.commit();

Check if debug mode:

SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
boolean isDebug = sharedPref.getBoolean("DEBUG_MODE", false);

if(isDebug){
    //Activate debug features
}else{
    //Disable debug features
}

Leave a Reply

Your email address will not be published. Required fields are marked *