我正在尝试编写一个测试用例来验证写入共享首选项的类。 我使用的是Android Studio v1.5。
在良好的旧日食中,当使用AndroidTestCase
时,第二个apk文件被部署到设备,并且可以使用检测上下文运行测试,因此您可以使用仪器apk来运行测试共享首选项而不改变apk的主要共享首选项文件。
我整个上午都试图弄清楚如何在Android Studio测试中获取非空上下文。显然,为eclipse进行的单元测试与Android Studio测试框架不兼容,因为调用getContext()
会返回null。
我以为我在这个问题上找到了答案:
Get context of test project in Android junit test case
随着旧版Android Studio没有完整的测试支持,情况已经发生了变化。所以很多答案都只是黑客攻击。显然现在不应该扩展InstrumentationTestCase
或AndroidTestCase
,而应该像这样编写测试:
@RunWith(AndroidJUnit4.class)
public class MyTest {
@Test
public void testFoo(){
Context instrumentationContext = InstrumentationRegistry.getContext();
Context mainProjectContext = InstrumentationRegistry.getTargetContext();
}
}
所以我现在有一个非null的检测上下文,getSharedPreferences
方法返回一个似乎有用的实例,但实际上没有写入首选项文件。
如果我这样做:
context = InstrumentationRegistry.getContext();
然后,SharedPreferences编辑器正确写入和提交,不会抛出任何异常。仔细观察后,我可以看到编辑器正在尝试写入此文件:
data/data/<package>.test/shared_prefs/PREFS_FILE_NAME.xml
但是文件永远不会被创建或写入。
然而使用这个:
context = InstrumentationRegistry.getTargetContext();
编辑器正常工作,首选项将写入此文件:
/data/data/<package>/shared_prefs/PREFS_FILE_NAME.xml
首选项以私有模式实例化:
SharedPreferences sharedPreferences = context.getSharedPreferences(fileName, Context.MODE_PRIVATE);
据我所知,运行测试后没有测试apk上传到设备。这可能解释了为什么文件不是使用检测上下文编写的。这个上下文是否可能是一个无声地失败的伪上下文?
如果是这种情况,我怎样才能获得真实的仪器上下文,以便我可以在不改变主项目的偏好的情况下编写首选项?
答案 0 :(得分:3)
事实证明你无法使用检测上下文写入共享首选项,即使在eclipse中也是如此。这将是eclipse的等效测试:
import android.content.Context;
import android.content.SharedPreferences;
import android.test.InstrumentationTestCase;
public class SharedPrefsTest extends InstrumentationTestCase {
public void test() throws Exception {
Context context = getInstrumentation().getContext();
String fileName = "FILE_NAME";
SharedPreferences sharedPreferences = context.getSharedPreferences(fileName, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("key", "value");
editor.commit();
SharedPreferences sharedPreferences2 = context.getSharedPreferences(fileName, Context.MODE_PRIVATE);
assertEquals("value", sharedPreferences2.getString("key", null));
}
}
我刚刚运行它也失败了。绝不会写首选项。我认为在此上下文中禁止内部存储文件访问,因为调用Context.getFilesDir()
会抛出InvocationTargetException,并且调用File.exists()
优先于首选项文件(您可以使用调试器检查编辑器写入哪个文件) ,只需在mFile
成员实例中查找名为this.$0
的私有变量。
所以我认为这实际上是可能的。我虽然过去曾使用过仪表上下文进行数据访问层测试,但实际上我们使用了主上下文(AndroidTestCase.getContext()
),尽管我们为首选项和SQLite文件使用了不同的名称。这就是为什么单元测试没有修改常规应用程序文件的原因。
答案 1 :(得分:2)
仪器将与您的应用程序一起安装。应用程序将自行运行,从而读取和编写自己的SharedPreferences
。
奇怪的是,SharedPreferences
的{{1}}被删除(或从未创建过),但即使它们被创建,您也很难将它们传递到您正在测试的应用程序中。如上所述,只需在您的应用内部调用Instrumentation
仍然可以提供实际的应用偏好设置,而不是您的设备偏好设置。
您需要找到一种方法来为您测试的应用程序提供首选项。一个很好的解决方案是将您的偏好设置保留在context.getSharedPreferences();
中,如下所示:
Application
这样,你可以
public class App extends Application {
SharedPreferences mPreferences;
public void onCreate() {
mPreferences = getSharedPreferences(fileName, Context.MODE_PRIVATE);
}
// add public getter / setter
}
在您的测试设置中,然后调用以下内容以注入您需要的任何首选项
((App) context.getApplicationContext()).getPreferences()
确保在每次测试后正确完成活动,以便每个测试都可以获得自己的依赖项。
此外,您应该强烈考虑使用Mockito,其他框架模拟@Before
public void before() {
((App) InstrumentationRegistry.getTargetContext()).setPreferences(testPreferences);
}
,或者只是自己实现SharedPreferences
界面,因为这极大地简化了验证与模型的交互。
答案 2 :(得分:0)
请注意,您仍然可以使用Android Studio运行在Android Studio中扩展InstrumentationTestCase
,AndroidTestCase
或ActivityInstrumentationTestCase2
的旧式测试。
新的测试支持库提供ActivityTestRule
来提供单个活动的功能测试。我相信在尝试获取用于测试的Instrumentation
和/或Context
之前,您需要创建其中一个对象。 The documentation for Espresso有一个使用此类的示例。
答案 3 :(得分:0)
如果你想在不创建活动的情况下测试类,我发现使用Robolectric是最简单的。 Robolectric为您提供了一个模拟上下文,它可以完成上下文所做的一切。事实上,这个背景是我使用Robolectric进行单元测试的主要原因。