如何检测Android应用程序是否正在使用Espresso运行UI测试

时间:2015-02-16 21:08:37

标签: android android-espresso ui-testing

我正在为Android编写一些Espresso测试。我正在运行以下问题:

为了让某个测试用例正常运行,我需要在应用程序中禁用某些功能。因此,在我的应用程序中,我需要检测我是否正在运行Espresso测试,以便我可以禁用它。但是,我不想使用BuildConfig.DEBUG,因为我不希望在调试版本中禁用这些功能。另外,我想避免创建一个新的buildConfig来避免创建太多的构建变体(我们已经定义了很多版本)。

我一直在寻找一种方法来定义buildConfigField以供测试,但我无法在Google上找到任何引用。

9 个答案:

答案 0 :(得分:30)

结合CommonsWare的回答。这是我的解决方案:

我定义了一个AtomicBoolean变量和一个函数来检查它是否正在运行测试:

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 ();
}

这样可以避免每次需要检查值时都进行try-catch检查,并且只在第一次调用此函数时才进行检查。

答案 1 :(得分:24)

结合使用Commonsware评论和Comtaler的解决方案,这是使用Espresso框架为任何测试类做到这一点的方法。

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();
    }

答案 2 :(得分:12)

在上述答案的基础上,以下Kotlin代码是等效的:

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

然后您可以检查属性的值:

if (isRunningTest) {
  // Espresso only code
}

答案 3 :(得分:5)

BuildConfig类中的标志呢?

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

将此内容添加到您的测试班级中。

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

答案 4 :(得分:3)

我不想使用在android上慢的反射。我们大多数人都设置了dagger2用于依赖注入。我有一个测试组件用于测试。以下是您可以获得应用程序模式(测试或正常)的简短方法:

创建一个枚举:

public enum ApplicationMode {
    NORMAL,TESTING;
}

和正常的AppModule:

@Module
public class AppModule {

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

像我一样创建一个测试跑步者:

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);
    }
}

别忘了像这样在gradle中声明:

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

现在使用覆盖方法创建AppModule的子类,该方法看起来与此完全相同,并且不要将其标记为类定义之上的模块:

public class TestAppModule extends AppModule{

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

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

现在,您在自定义测试运行器中声明的MyTestApplication类中声明了以下内容:

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();
    }
}

现在使用它只需将其注入生产代码中,无论如何:

@Inject
    ApplicationMode appMode;

因此,当您运行espresso测试时,它将测试枚举,但在生产代码中,它将是正常的枚举。

ps没有必要,但如果你需要看看我的生产匕首如何构建图形,就像这样并在应用程序子类中声明:

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

答案 5 :(得分:2)

我将创建两个文件,如下所示

  

src / main /.../ Injection.java

     

的src / androidTest /.../ Injection.java

在Injection.java中,我将使用不同的实现,或者仅使用静态变量。

由于androidTest是源集,而不是构建类型的一部分,我认为你想做的事情很难。

答案 6 :(得分:1)

您可以使用SharedPreferences。

设置调试模式:

boolean isDebug = true;

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

检查调试模式:

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

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

答案 7 :(得分:0)

如果您将 JitPack 与kotlin一起使用。您需要更改 Espresso的包装名称。

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

用于检查

if (isRunningTest) {
  // Espresso only code
}

答案 8 :(得分:0)

这里有一种方法可以为 react-native Android 应用调整公认的解决方案。

// MainActivity.java

// ...

  @Override
  protected ReactActivityDelegate createReactActivityDelegate() {
    return new ReactActivityDelegate(this, getMainComponentName()) {

      // ...

      @Override
      protected Bundle getLaunchOptions() {
        Bundle initialProperties = new Bundle();
        boolean testingInProgress;

        try {
          Class.forName ("androidx.test.espresso.Espresso");
          testingInProgress = true;
        } catch (ClassNotFoundException e) {
          testingInProgress = false;
        }

        initialProperties.putBoolean("testingInProgress", testingInProgress);

        return initialProperties;
      }
    };
  }
}

然后,您将能够访问 testingInProgress 作为提供给最顶层组件(通常为 App.js)的道具。从那里您可以使用 componentDidMount 或等效物来访问它并将其放入您的 Redux 商店(或您正在使用的任何东西),以便您的应用程序的其余部分可以访问它。

我们使用它来触发我们应用中的一些逻辑,以帮助我们使用 fastlane 截取屏幕截图。