我进行了一个简单的测试,以验证从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/

10-10 01:40