我从google的示例中获得了灵感,通过创建一个SharedPreferencesHelper
类来测试SharedPreferences
代码:
https://github.com/googlesamples/android-https://github.com/googlesamples/android-testing/blob/master/unit/BasicSample/app/src/main/java/com/example/android/testing/unittesting/BasicSample/SharedPreferencesHelper.java
可以看到,该类使用类中硬编码的实际字符串作为sharedPreferences
的键--下面是该类的摘录:
public class SharedPreferencesHelper {
// Keys for saving values in SharedPreferences.
static final String KEY_NAME = "key_name";
static final String KEY_DOB = "key_dob_millis";
static final String KEY_EMAIL = "key_email";
public boolean savePersonalInfo(SharedPreferenceEntry sharedPreferenceEntry){
// Start a SharedPreferences transaction.
SharedPreferences.Editor editor = mSharedPreferences.edit();
editor.putString(KEY_NAME, sharedPreferenceEntry.getName());
editor.putLong(KEY_DOB, sharedPreferenceEntry.getDateOfBirth().getTimeInMillis());
editor.putString(KEY_EMAIL, sharedPreferenceEntry.getEmail());
// Commit changes to SharedPreferences.
return editor.commit();
}
在这里使用SharedPreferencesHelperTest
类进行测试时,他们使用上述类中定义的相同变量访问模拟的sharedPreferences
:
https://github.com/googlesamples/android-testing/blob/master/unit/BasicSample/app/src/test/java/com/example/android/testing/unittesting/BasicSample/SharedPreferencesHelperTest.java
该类的摘录如下所示:
when(mMockSharedPreferences.getString(eq(SharedPreferencesHelper.KEY_NAME), anyString()))
.thenReturn(mSharedPreferenceEntry.getName());
when(mMockSharedPreferences.getString(eq(SharedPreferencesHelper.KEY_EMAIL), anyString()))
.thenReturn(mSharedPreferenceEntry.getEmail());
when(mMockSharedPreferences.getLong(eq(SharedPreferencesHelper.KEY_DOB), anyLong()))
.thenReturn(mSharedPreferenceEntry.getDateOfBirth().getTimeInMillis());
google这样做的方式允许他们绕过使用context
从string.xml资源文件中提取字符串并在sharedPreferences
中查询它的问题,就像使用getString()
通常假设的那样,即:
mSharedPreferences.getString(context.getString(R.string.name),"");
但是,我读到过不能使用context.getString
,因为它是final方法,mockito不能模拟final方法:
Mockito -重写接受基元参数的方法
然后我如何使用mockito对任何使用getString
的方法进行单元测试?任何使用getString
的方法都将无法工作,我的单元测试将失败。
这是我为SharedPreferences
编写的类,我想用getStrings
编写的sharedPreferences
密钥测试它:
public class SharedPreferencesHelper {
// The injected SharedPreferences implementation to use for persistence.
private final SharedPreferences mSharedPreferences;
private Context context;
public SharedPreferencesHelper(SharedPreferences sharedPreferences, Context context) {
mSharedPreferences = sharedPreferences;
this.context = context;
}
public boolean saveName(String name) {
// Start a SharedPreferences transaction.
SharedPreferences.Editor editor = mSharedPreferences.edit();
editor.putString(context.getString(R.string.name), name);
return editor.commit();
}
public String fetchName() {
// Start a SharedPreferences transaction.
return mSharedPreferences.getString(context.getString(R.string.name),"");
}
public boolean saveGender(String gender) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
editor.putString(context.getString(R.string.gender), gender);
return editor.commit();
}
}
这是我为上面的类编写的测试--它非常类似于google的SharedPreferencesHelperTest:
@RunWith(MockitoJUnitRunner.class)
public class SharedPreferencesHelperTest {
private static final String TEST_NAME = "Test name";
private SharedPreferencesHelper mMockSharedPreferencesHelper;
@Mock
SharedPreferences mMockSharedPreferences;
@Mock
SharedPreferences.Editor mMockEditor;
@Mock
MockContext context;
@Before
public void setUp() throws Exception {
// Create a mocked SharedPreferences.
mMockSharedPreferencesHelper = createMockSharedPreference();
}
@Test
public void testSaveName() throws Exception {
boolean success = mMockSharedPreferencesHelper.saveName(TEST_NAME);
Timber.e("success " + success);
assertThat("Checking that name was saved... returns true = " + success,
success, is(true));
String name = mMockSharedPreferencesHelper.fetchName();
Timber.e("name " + name);
assertThat("Checking that name has been persisted and read correctly " + name,
TEST_NAME,
is(name));
}
/**
* Creates a mocked SharedPreferences.
*/
private SharedPreferencesHelper createMockSharedPreference() {
// Mocking reading the SharedPreferences as if mMockSharedPreferences was previously written
// correctly.
when(mMockSharedPreferences.getString(Matchers.eq("name"), anyString()))
.thenReturn(TEST_NAME);
when(mMockSharedPreferences.getString(Matchers.eq("gender"), anyString()))
.thenReturn("M");
// Mocking a successful commit.
when(mMockEditor.commit()).thenReturn(true);
// Return the MockEditor when requesting it.
when(mMockSharedPreferences.edit()).thenReturn(mMockEditor);
return new SharedPreferencesHelper(mMockSharedPreferences, context);
}
}
运行测试失败,因为我在SharedPreferencesHelper
类中使用了getString
。如果我硬编码密钥,我将不会得到错误,即:
public String fetchName() {
// Start a SharedPreferences transaction.
return mSharedPreferences.getString("name","");
}
不应该在代码中硬编码字符串,那么我该如何解决这个难题呢?
4条答案
按热度按时间yebdmbv41#
您可以通过不使用
Context
获取字符串资源来解决此问题,但是Resources
类.Resources
具有相同的getString
方法,但它不是最终的。https://developer.android.com/reference/android/content/res/Resources.html#getString(int)
或者,您可以尝试Robolectric,它将解决这个问题,因为它们实现了上下文。
http://robolectric.org/
uxh89sit2#
2017年6月答复:
我听从了jbarat的建议使用Robolectric。这是一个有点陡峭的曲线开始,因为事情得到改变/弃用/删除/不兼容的每一个版本的Robolectric/Android Studio。
以下是我在使用Android Studio 2.3.3和Robolectric 3.3.2时为实现这一功能所做的工作:
getString
的所有方法都应该接受Context
作为第一个参数,以使其可模拟Context
build.gradle
:MyClass.java
:试验:
ercv8c1e3#
要启用final方法的模拟,您需要Mockito v2,并按照此处的说明Mock the unmockable:最终类/方法的选择性模拟
在
src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
中创建一个包含单行的文件:完成此操作后,可以模拟getString()
when(mMockContext.getString(R.string.string_name)).thenReturn("Mocked String");
fslejnso4#
我希望这是模拟字符串资源的可能性之一。
将此添加到测试类