Espresso登录测试时在Activity创建前修改SharedPreferences的问题问询
问题根因
- 你调用
getInstrumentation().getContext()获取的是测试安装包的上下文,修改的是测试包的SharedPreferences,完全不会影响业务App的登录配置,所以之前的清空逻辑实际没有生效 - 标准
ActivityScenarioRule默认会在@Before方法执行前就自动启动目标Activity,所以你在@Before里修改配置的时候,LoginActivity已经走完了登录校验逻辑,早就跳转到主页面 - 你在
@Before里重复初始化ActivityScenarioRule属于无效操作,不会重启已经启动的Activity
修复步骤
1. 改用懒加载模式的ActivityScenarioRule
关闭自动启动开关,等你清空完配置之后再手动启动Activity
2. 修正SharedPreferences的上下文
用getInstrumentation().getTargetContext()获取被测App的上下文,修改的才是App实际读取的配置文件
3. 测试结束后统一清空登录状态
避免成功登录的测试跑完后残留状态影响后续用例
修正后的完整代码
package com.androidapp.zitgenius; import static androidx.test.espresso.Espresso.onView; import android.content.Context; import android.content.SharedPreferences; import android.util.Log; import android.view.View; import androidx.test.espresso.intent.Intents; import androidx.test.ext.junit.rules.ActivityScenarioRule; import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import static androidx.test.espresso.action.ViewActions.click; import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard; import static androidx.test.espresso.action.ViewActions.typeText; import static androidx.test.espresso.assertion.ViewAssertions.matches; import static androidx.test.espresso.intent.Intents.intended; import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent; import static androidx.test.espresso.matcher.ViewMatchers.withId; import static androidx.test.espresso.matcher.ViewMatchers.withText; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; /** * Instrumented test of Login Activity, which will execute on an Android device. */ @RunWith(AndroidJUnit4.class) public class LoginActivityTest{ public final String INCORRECT_USERNAME = "this isn't a real username"; public final String INCORRECT_PASSWORD = "this isn't a real password"; public final String CORRECT_USERNAME = "test2@l.com"; public final String CORRECT_PASSWORD = "123"; private static final String SHARE_PREF_NAME ="login_ref" ; private static final String KEY_USER_ID ="id" ; public void setSharedPrefs(){ // 改为获取被测App的上下文 SharedPreferences sharedPreferences = getInstrumentation().getTargetContext().getSharedPreferences(SHARE_PREF_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit() ; editor.putInt(KEY_USER_ID, -1) ; // 用commit确保同步写入完成,避免启动Activity的时候还没写完 editor.commit(); Log.i("TESTING","Setting up shared prefs"); } // 第三个参数false关闭自动启动Activity @Rule public ActivityScenarioRule<LoginActivity> activityScenarioRule = new ActivityScenarioRule<>(LoginActivity.class, false, false); private View decorView; @Before public void setUp() { Intents.init(); // 先清空登录状态 setSharedPrefs(); // 再手动启动Activity activityScenarioRule.getScenario().launch(null); } @Test public void inputLoginJustUsername() { SharedPreferences sharedPreferences = getInstrumentation().getTargetContext().getSharedPreferences(SHARE_PREF_NAME, Context.MODE_PRIVATE); Log.i("TESTING",String.valueOf(sharedPreferences.getInt(KEY_USER_ID, -1))); //Type text and then hit Login Button onView(withId(R.id.editText_username_input)).perform(typeText(CORRECT_USERNAME), closeSoftKeyboard()); onView(withId(R.id.button_login)).perform(click()); // Confirm inputted text remains onView(withId(R.id.editText_username_input)).check(matches(withText(CORRECT_USERNAME))); //TODO: Confirm Toast error message } @Test public void inputLoginJustPassword() { //Type text and then hit Login Button onView(withId(R.id.editText_password_input)).perform(typeText(CORRECT_PASSWORD), closeSoftKeyboard()); onView(withId(R.id.button_login)).perform(click()); // Confirm inputted text remains onView(withId(R.id.editText_password_input)).check(matches(withText(CORRECT_PASSWORD))); //TODO: Confirm Toast error message } @Test public void inputLoginIncorrectCredentials() { //Type text and then hit Login Button onView(withId(R.id.editText_username_input)).perform(typeText(INCORRECT_USERNAME), closeSoftKeyboard()); onView(withId(R.id.editText_password_input)).perform(typeText(INCORRECT_PASSWORD), closeSoftKeyboard()); onView(withId(R.id.button_login)).perform(click()); // Confirm inputted text remains onView(withId(R.id.editText_username_input)).check(matches(withText(INCORRECT_USERNAME))); onView(withId(R.id.editText_password_input)).check(matches(withText(INCORRECT_PASSWORD))); //TODO: Confirm Toast error message } @Test public void inputLoginCorrectCredentials() { Log.i("TESTING", "Starting correct cred test"); int totalMillisToWait = 3000; int pollEveryMillis = 300; //Type text and then hit Login Button onView(withId(R.id.editText_username_input)).perform(typeText(CORRECT_USERNAME), closeSoftKeyboard()); onView(withId(R.id.editText_password_input)).perform(typeText(CORRECT_PASSWORD), closeSoftKeyboard()); onView(withId(R.id.button_login)).perform(click()); //Wait for response for(int i = 0; i < totalMillisToWait/pollEveryMillis; i++){ try { Thread.sleep((long)pollEveryMillis); } catch(InterruptedException e) { System.out.println("got interrupted!"); } } // Confirm transition to NavBarActivity Log.i("TESTING", "Checking intents"); intended(hasComponent(BottomNavigationActivity.class.getName())); } @Test public void switchToSignupActivity() { //Hit button to switch to signup onView(withId(R.id.button_switch_signup)).perform(click()); // Confirm transition to Signup Activity intended(hasComponent(SignupActivity.class.getName())); } @After public void tearDown() throws Exception { Intents.release(); // 测试结束后清空登录状态 setSharedPrefs(); } }
可选优化
你代码中用Thread.sleep等待登录结果的方式不够稳定,可以替换为Espresso的IdlingResource实现异步等待,避免固定时长等待导致的用例偶现失败。
内容的提问来源于stack exchange,提问作者man_idk
相关产品推荐
相关产品推荐

