在为 Espresso 测试创建 activity 之前更改共享首选项
Altering shared preferences before creating activity for Espresso testing
我正在尝试为登录页面创建 Espresso 测试,但我 运行 遇到一个问题,一旦涉及成功登录的测试运行,任何后续测试现在都出错 activity 和重新 运行 登录测试需要完全重新启动虚拟设备以擦除共享首选项(这是因为登录 activity 检查所述首选项以查看是否已成功登录并跳过到应用程序主页面)。
我的问题是,一旦创建了 ActivityScenarioRule,更改共享首选项已经为时已晚,因为 activity 已经更改为主页。这是我的代码:
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.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@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(){
SharedPreferences sharedPreferences = getInstrumentation().getContext().getSharedPreferences(SHARE_PREF_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit() ;
editor.putInt(KEY_USER_ID, -1) ;
editor.apply(); //this will prevent automatic logins from disrupting tests
Log.i("TESTING","Setting up shared prefs");
}
@Rule
public ActivityScenarioRule<LoginActivity> activityScenarioRule
= new ActivityScenarioRule<>(LoginActivity.class);
private View decorView;
@Before
public void setUp() {
Intents.init();
setSharedPrefs();
activityScenarioRule
= new ActivityScenarioRule<>(LoginActivity.class);
}
@Test
public void inputLoginJustUsername() {
SharedPreferences sharedPreferences = getInstrumentation().getContext().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();
}
}
我认为您的问题在于使用 ActivityScenarioRule
,我认为它会在您声明后立即为您启动 Activity,我总是发现它有问题,因此不使用该规则。
我想你可以尝试两件事:
1 - 将您的首选项设置逻辑提取到规则中并确保它首先被调用:
// Here SharedPreferencesRule is a custom rule based on your "setSharedPrefs" function
// I'll leave that as an exercise to the reader
@Rule
public RuleChain rules = RuleChain.outer(new SharedPreferencesRule())
.around(new ActivityScenarioRule<>(LoginActivity.class))
并更新 Before
中的逻辑:
@Before
public void setUp() {
Intents.init();
// setSharedPrefs(); // This would no longer exist, done by the rule
// activityScenarioRule // Delete this
// = new ActivityScenarioRule<>(LoginActivity.class);
}
2 - 将 ActivityScenarioRule
替换为手动管理的方案
只需手动启动和控制场景,也许在您的设置中有类似的行为:
// Remove this rule
// @Rule
// public ActivityScenarioRule<LoginActivity> activityScenarioRule
// = new ActivityScenarioRule<>(LoginActivity.class);
private ActivityScenario<LoginActivity> scenario = null;
private View decorView;
@Before
public void setUp() {
Intents.init();
setSharedPrefs(); // Set prefs before launching activity
scenario = ActivityScenario.launch(LoginActivity.class);
}
@After
public void tearDown() throws Exception {
Intents.release();
scenario.close();
}
我正在尝试为登录页面创建 Espresso 测试,但我 运行 遇到一个问题,一旦涉及成功登录的测试运行,任何后续测试现在都出错 activity 和重新 运行 登录测试需要完全重新启动虚拟设备以擦除共享首选项(这是因为登录 activity 检查所述首选项以查看是否已成功登录并跳过到应用程序主页面)。
我的问题是,一旦创建了 ActivityScenarioRule,更改共享首选项已经为时已晚,因为 activity 已经更改为主页。这是我的代码:
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.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@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(){
SharedPreferences sharedPreferences = getInstrumentation().getContext().getSharedPreferences(SHARE_PREF_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit() ;
editor.putInt(KEY_USER_ID, -1) ;
editor.apply(); //this will prevent automatic logins from disrupting tests
Log.i("TESTING","Setting up shared prefs");
}
@Rule
public ActivityScenarioRule<LoginActivity> activityScenarioRule
= new ActivityScenarioRule<>(LoginActivity.class);
private View decorView;
@Before
public void setUp() {
Intents.init();
setSharedPrefs();
activityScenarioRule
= new ActivityScenarioRule<>(LoginActivity.class);
}
@Test
public void inputLoginJustUsername() {
SharedPreferences sharedPreferences = getInstrumentation().getContext().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();
}
}
我认为您的问题在于使用 ActivityScenarioRule
,我认为它会在您声明后立即为您启动 Activity,我总是发现它有问题,因此不使用该规则。
我想你可以尝试两件事:
1 - 将您的首选项设置逻辑提取到规则中并确保它首先被调用:
// Here SharedPreferencesRule is a custom rule based on your "setSharedPrefs" function
// I'll leave that as an exercise to the reader
@Rule
public RuleChain rules = RuleChain.outer(new SharedPreferencesRule())
.around(new ActivityScenarioRule<>(LoginActivity.class))
并更新 Before
中的逻辑:
@Before
public void setUp() {
Intents.init();
// setSharedPrefs(); // This would no longer exist, done by the rule
// activityScenarioRule // Delete this
// = new ActivityScenarioRule<>(LoginActivity.class);
}
2 - 将 ActivityScenarioRule
替换为手动管理的方案
只需手动启动和控制场景,也许在您的设置中有类似的行为:
// Remove this rule
// @Rule
// public ActivityScenarioRule<LoginActivity> activityScenarioRule
// = new ActivityScenarioRule<>(LoginActivity.class);
private ActivityScenario<LoginActivity> scenario = null;
private View decorView;
@Before
public void setUp() {
Intents.init();
setSharedPrefs(); // Set prefs before launching activity
scenario = ActivityScenario.launch(LoginActivity.class);
}
@After
public void tearDown() throws Exception {
Intents.release();
scenario.close();
}