Android Studio:无法在已检测的测试

时间:2016-02-03 15:08:07

标签: android unit-testing android-studio android-context instrumentation

我正在尝试编写一个测试用例来验证写入共享首选项的类。 我使用的是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没有完整的测试支持,情况已经发生了变化。所以很多答案都只是黑客攻击。显然现在不应该扩展InstrumentationTestCaseAndroidTestCase,而应该像这样编写测试:

@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上传到设备。这可能解释了为什么文件不是使用检测上下文编写的。这个上下文是否可能是一个无声地失败的伪上下文?

如果是这种情况,我怎样才能获得真实的仪器上下文,以便我可以在不改变主项目的偏好的情况下编写首选项?

4 个答案:

答案 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

这样,你可以

  1. 只要您有上下文,就可以使用public class App extends Application { SharedPreferences mPreferences; public void onCreate() { mPreferences = getSharedPreferences(fileName, Context.MODE_PRIVATE); } // add public getter / setter }
  2. 从单个来源获取偏好设置
  3. 在运行测试之前自己设置首选项并开始任何注入测试数据的活动。
  4. 在您的测试设置中,然后调用以下内容以注入您需要的任何首选项

    ((App) context.getApplicationContext()).getPreferences()

    确保在每次测试后正确完成活动,以便每个测试都可以获得自己的依赖项。

    此外,您应该强烈考虑使用Mockito,其他框架模拟@Before public void before() { ((App) InstrumentationRegistry.getTargetContext()).setPreferences(testPreferences); } ,或者只是自己实现SharedPreferences界面,因为这极大地简化了验证与模型的交互。

答案 2 :(得分:0)

请注意,您仍然可以使用Android Studio运行在Android Studio中扩展InstrumentationTestCaseAndroidTestCaseActivityInstrumentationTestCase2的旧式测试。

新的测试支持库提供ActivityTestRule来提供单个活动的功能测试。我相信在尝试获取用于测试的Instrumentation和/或Context之前,您需要创建其中一个对象。 The documentation for Espresso有一个使用此类的示例。

答案 3 :(得分:0)

如果你想在不创建活动的情况下测试类,我发现使用Robolectric是最简单的。 Robolectric为您提供了一个模拟上下文,它可以完成上下文所做的一切。事实上,这个背景是我使用Robolectric进行单元测试的主要原因。