You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 09:27:03