我进行了一个简单的测试,以验证从SharedPreferences读取的数据是否在UI上正确显示。数据的获取和显示操作均在Activity的onResume()方法中执行。
但是问题是,即使我模拟了首选项对象并定义了伪造的返回值,该活动仍会从真实的首选项中读取数据,而忽略了when(...).thenReturn(...)
语句。有人有什么想法吗?
@RunWith(AndroidJUnit4.class)
public class EditProfileActivityTest {
@Mock
private UserPreference userPreference;
private String FAKE_NAME = "Test";
@Rule
public ActivityTestRule<EditProfileActivity> activityTestRule = new ActivityTestRule(EditProfileActivity.class,true,false);
@Before
public void setUp(){
//Set fake SharedPreferences
MockitoAnnotations.initMocks(this);
when(userPreference.getName()).thenReturn(FAKE_NAME);
//Start Activity
Intent intent = new Intent();
activityTestRule.launchActivity(intent);
}
@Test
public void showUserData() throws Exception{
onView(withId(R.id.name_tv)).check(matches(withText(FAKE_NAME)));
}
}
其中UserPreference是一个自定义类,仅包装了SharedPreference类并包含许多getter和setters。这是其构造函数
public UserPreference(Context context) {
this.context = context;
sharedPreferences = this.context.getSharedPreferences("Pref", Context.MODE_PRIVATE);
prefEditor = sharedPreferences.edit();
}
及其获取器和设置器之一:
public String getName() {
return sharedPreferences.getString(context.getString(R.string.pref_name), "Guest");
}
public void saveName(String name){
prefEditor.putString(context.getString(R.string.pref_name), name);
prefEditor.apply();
}
[编辑]
我原始活动的简化版:
public class EditProfileActivity extends AppCompatActivity{
//...
private UserPreference userPreference;
//...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
userPreference = new UserPreference(this);
setContentView(R.layout.activity_edit_profile);
//...
}
@Override
protected void onResume() {
super.onResume();
//...
String name = userPreference.getName();
nameEdt.setText(name); //Display the name on an EditText
//...
}
}
最佳答案
UserPreference
模拟已创建,但活动仍使用在其onCreate
方法中创建的模拟。您需要将该活动的userPreference
字段设置为模拟对象。
有几种方法可以做到这一点:
为userPreference
字段添加一个setter方法,并在您的@Before
方法中调用它:
@Before
public void setUp(){
...
EditProfileActivity activity = activityTestRule.launchActivity(intent);
activity.setUserPreference(mockedUserPreference);
}
这很简单但是很丑:您仅更改活动以适应测试。
通过反射设置
userPreference
字段:@Before
public void setUp(){
...
EditProfileActivity activity = activityTestRule.launchActivity(intent);
Field userPreferenceField = activity.getClass().getDeclaredField("userPreference");
field.setAccessible(true);
userPreferenceField.set(activity, mockedUserPreference);
}
这是一个脆弱的测试:更改字段名称会破坏它而不会产生编译错误。但是,不必更改活动,因此在您无法更改时很有用。
不要在
UserPreference
方法中实例化onCreate
。在普通的Java中,我会将其添加为构造函数参数,但是我不知道在Android中是否可以轻松地实现。也许使用依赖注入框架,它们非常适合与模拟一起使用:Android and Dependency Injection关于java - 该测试仍然从真实的SharedPreferences中读取,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/33750175/