Mockito:如何在mockito中使用getString?

nle07wnf  于 2023-02-16  发布在  其他
关注(0)|答案(4)|浏览(223)

我从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这样做的方式允许他们绕过使用contextstring.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","");
}

不应该在代码中硬编码字符串,那么我该如何解决这个难题呢?

yebdmbv4

yebdmbv41#

您可以通过不使用Context获取字符串资源来解决此问题,但是Resources类. Resources具有相同的getString方法,但它不是最终的。
https://developer.android.com/reference/android/content/res/Resources.html#getString(int)
或者,您可以尝试Robolectric,它将解决这个问题,因为它们实现了上下文。
http://robolectric.org/

uxh89sit

uxh89sit2#

2017年6月答复:

我听从了jbarat的建议使用Robolectric。这是一个有点陡峭的曲线开始,因为事情得到改变/弃用/删除/不兼容的每一个版本的Robolectric/Android Studio。
以下是我在使用Android Studio 2.3.3和Robolectric 3.3.2时为实现这一功能所做的工作:

  • 代码中使用getString的所有方法都应该接受Context作为第一个参数,以使其可模拟
  • 测试需要正确注解,以便使用Robolectric运行,而不是使用默认的JUnit runner
  • 测试需要注入Robolectric提供的模拟Context
  • 需要配置Android Studio,请参阅
  • http://robolectric.org/getting-started/#building-with-android-studio
  • https://github.com/robolectric/robolectric.github.io/pull/41/files

build.gradle

testCompile "org.robolectric:robolectric:3.3.2"
testCompile "org.robolectric:shadows-multidex:3.3.2"

MyClass.java

public static String myGetString(Context context) {
    return context.getString(R.string.my_string);
}

试验:

@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, packageName = "com.myapplication", sdk = 19)
public class MyTest {
    private Context mockContext;

    @Before
    public void setUp() {
        // inject context provided by Robolectric
        mockContext = RuntimeEnvironment.application.getApplicationContext();
    }

    @Test
    public void test_myGetString() {
        assertEquals("MY STRING", MyClass.myGetString(mockContext));
    }
}
ercv8c1e

ercv8c1e3#

要启用final方法的模拟,您需要Mockito v2,并按照此处的说明Mock the unmockable:最终类/方法的选择性模拟
src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker中创建一个包含单行的文件:

mock-maker-inline

完成此操作后,可以模拟getString()when(mMockContext.getString(R.string.string_name)).thenReturn("Mocked String");

fslejnso

fslejnso4#

我希望这是模拟字符串资源的可能性之一。

applicationMock = Mockito.mock(BaseApplication::class.java)
`when`(applicationMock.getString(R.string.primary_contact)).thenReturn("Primary Contact")

将此添加到测试类

@RunWith(RobolectricTestRunner::class)

相关问题