如何检测 android app 是否 运行 UI 用 Espresso 测试
How to detect whether android app is running UI test with Espresso
我正在为 Android 编写一些 Espresso 测试。我 运行 正在解决以下问题:
为了让某个测试用例正常运行,我需要禁用应用程序中的某些功能。因此,在我的应用程序中,我需要检测我是否正在 运行ning Espresso 测试,以便我可以禁用它。但是,我不想使用 BuildConfig.DEBUG
因为我不想在调试版本中禁用这些功能。另外,我想避免创建一个新的 buildConfig 以避免创建太多构建变体(我们已经定义了很多口味)。
我正在寻找一种方法来定义 buildConfigField 以进行测试,但我在 Google 上找不到任何参考。
您可以为此使用 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
}
结合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 检查,它只在您第一次调用此函数时运行检查。
结合 Commonsware 评论 + Comtaler 的解决方案,这是使用 Espresso 框架进行任何测试的方法class。
private static AtomicBoolean isRunningTest;
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();
}
基于以上答案,以下 Kotlin 代码是等效的:
val isRunningTest : Boolean by lazy {
try {
Class.forName("android.support.test.espresso.Espresso")
true
} catch (e: ClassNotFoundException) {
false
}
}
然后您可以检查 属性 的值:
if (isRunningTest) {
// Espresso only code
}
我不喜欢使用在 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 的子class,看起来完全像这样,不要将其标记为 class 定义之上的模块:
public class TestAppModule extends AppModule{
public TestAppModule(Application application) {
super(application);
}
@Override
public ApplicationMode provideApplicationMode(){
return ApplicationMode.TESTING; //notice we are testing here
}
}
现在在您在自定义测试运行程序中声明的 MyTestApplication class 中声明了以下内容:
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 不是必需的,但如果您需要查看我的生产匕首如何构建图形,它是这样的并在应用程序中声明 subclass:
protected AppComponent initDagger(Application application) {
return DaggerAppComponent.builder()
.appModule(new AppModule(application))
.build();
}
我将创建如下两个文件
src/main/.../Injection.java
src/androidTest/.../Injection.java
而在 Injection.java 中,我将使用不同的实现,或者只是一个静态变量 int。
由于 androidTest 是源集,而不是构建类型的一部分,我认为你想做的事情很难。
BuildConfig
class 中的标志怎么样?
android {
defaultConfig {
// No automatic import :(
buildConfigField "java.util.concurrent.atomic.AtomicBoolean", "IS_TESTING", "new java.util.concurrent.atomic.AtomicBoolean(false)"
}
}
在你的测试中的某处添加这个 classes.
static {
BuildConfig.IS_TESTING.set(true);
}
如果您将 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
}
这是一种为 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 截取屏幕截图。
我建议在另一个 class 调用中使用初始化为 false
的布尔变量,例如 Settings.java
:
private static boolean isRunningAndroidTest = false;
此布尔变量将具有以下 setter 和 getter 也在 Settings.java
中定义:
public static void setIsRunningAndroidTest(boolean isRunningAndroidTest) {
Settings.isRunningAndroidTest = isRunningAndroidTest;
}
public static boolean getIsRunningAndroidTest() {
return isRunningAndroidTest;
}
然后可以通过调用 Settings.java
中定义的 setter 在 androidTest 文件的开头将此 isRunningAndroidTest
变量切换为 true
,如下所示:
Settings.setIsRunningAndroidTest(true);
最后,此布尔变量的实际值稍后可以通过调用其在 Settings.java
中定义的相应 getter 来在任何其他文件中检查,如下所示:
if (Settings.getIsRunningAndroidTest()) {
// Do something in case an androidTest is currently running
} else {
// Do something else in case NO androidTest is currently running
}
我正在为 Android 编写一些 Espresso 测试。我 运行 正在解决以下问题:
为了让某个测试用例正常运行,我需要禁用应用程序中的某些功能。因此,在我的应用程序中,我需要检测我是否正在 运行ning Espresso 测试,以便我可以禁用它。但是,我不想使用 BuildConfig.DEBUG
因为我不想在调试版本中禁用这些功能。另外,我想避免创建一个新的 buildConfig 以避免创建太多构建变体(我们已经定义了很多口味)。
我正在寻找一种方法来定义 buildConfigField 以进行测试,但我在 Google 上找不到任何参考。
您可以为此使用 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
}
结合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 检查,它只在您第一次调用此函数时运行检查。
结合 Commonsware 评论 + Comtaler 的解决方案,这是使用 Espresso 框架进行任何测试的方法class。
private static AtomicBoolean isRunningTest;
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();
}
基于以上答案,以下 Kotlin 代码是等效的:
val isRunningTest : Boolean by lazy {
try {
Class.forName("android.support.test.espresso.Espresso")
true
} catch (e: ClassNotFoundException) {
false
}
}
然后您可以检查 属性 的值:
if (isRunningTest) {
// Espresso only code
}
我不喜欢使用在 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 的子class,看起来完全像这样,不要将其标记为 class 定义之上的模块:
public class TestAppModule extends AppModule{
public TestAppModule(Application application) {
super(application);
}
@Override
public ApplicationMode provideApplicationMode(){
return ApplicationMode.TESTING; //notice we are testing here
}
}
现在在您在自定义测试运行程序中声明的 MyTestApplication class 中声明了以下内容:
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 不是必需的,但如果您需要查看我的生产匕首如何构建图形,它是这样的并在应用程序中声明 subclass:
protected AppComponent initDagger(Application application) {
return DaggerAppComponent.builder()
.appModule(new AppModule(application))
.build();
}
我将创建如下两个文件
src/main/.../Injection.java
src/androidTest/.../Injection.java
而在 Injection.java 中,我将使用不同的实现,或者只是一个静态变量 int。
由于 androidTest 是源集,而不是构建类型的一部分,我认为你想做的事情很难。
BuildConfig
class 中的标志怎么样?
android {
defaultConfig {
// No automatic import :(
buildConfigField "java.util.concurrent.atomic.AtomicBoolean", "IS_TESTING", "new java.util.concurrent.atomic.AtomicBoolean(false)"
}
}
在你的测试中的某处添加这个 classes.
static {
BuildConfig.IS_TESTING.set(true);
}
如果您将 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
}
这是一种为 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 截取屏幕截图。
我建议在另一个 class 调用中使用初始化为 false
的布尔变量,例如 Settings.java
:
private static boolean isRunningAndroidTest = false;
此布尔变量将具有以下 setter 和 getter 也在 Settings.java
中定义:
public static void setIsRunningAndroidTest(boolean isRunningAndroidTest) {
Settings.isRunningAndroidTest = isRunningAndroidTest;
}
public static boolean getIsRunningAndroidTest() {
return isRunningAndroidTest;
}
然后可以通过调用 Settings.java
中定义的 setter 在 androidTest 文件的开头将此 isRunningAndroidTest
变量切换为 true
,如下所示:
Settings.setIsRunningAndroidTest(true);
最后,此布尔变量的实际值稍后可以通过调用其在 Settings.java
中定义的相应 getter 来在任何其他文件中检查,如下所示:
if (Settings.getIsRunningAndroidTest()) {
// Do something in case an androidTest is currently running
} else {
// Do something else in case NO androidTest is currently running
}