некоторые тесты Robolectric терпят неудачу, если выполняются все вместе, но проходят по отдельности

У меня Android Studio 1.2, Robolectric 3.0-rc2.

У меня есть два тестовых класса, один из которых называется MotdTest с одним тестовым методом, который проверяет сериализацию и десериализацию POJO json. Другой называется UserInfoTest, который содержит 4 метода тестирования, которые проверяют информацию о пользователе, которую я установил в SharedPreferences. Если я запускаю UserInfoTest по отдельности, все 4 метода тестирования всегда проходят успешно. Однако, если я запускаю все тесты, тест в MotdTest завершается успешно, но два метода UserInfoTest всегда терпят неудачу. Я запускаю из командной строки прямо сейчас, вызывая ./gradlew test

Кто-нибудь знает, почему некоторые из моих тестов терпят неудачу, когда я запускаю все тесты? В моем UserInfoTest у меня правильно есть аннотированный метод @After, в котором я выполняю очистку, вызывая clear().commit() для SharedPreferences.Editor.

UserInfoTest:

testOnSignIn() терпит неудачу в assertThat(6, equalTo(prefs.getAll().size()));, потому что размер prefs равен 0.

И testIsSignedIn() терпит неудачу в assertThat(UserInfo.isSignedIn(), is(false));

@RunWith(MyRoboRunner.class)
@Config(constants = BuildConfig.class)
public class UserInfoTest {

    private String mExpectedId;

    private String mExpectedName;

    private String mExpectedEmail;

    private String mExpectedToken;

    private String mExpectedKey;

    @Before
    public void setUp() throws Exception {
        ShadowLog.stream = System.out;

        mExpectedId = "someiD";
        mExpectedName = "johnny boy";
        mExpectedEmail = "[email protected]";
        mExpectedToken = "Session Token";
        mExpectedKey = "Session Key";
    }

    @After
    public void tearDown() {
        SharedPreferences prefs = RuntimeEnvironment.application.getSharedPreferences(
                UserInfo.PREFERENCES, Context.MODE_PRIVATE);
        prefs.edit().clear().commit();
        mExpectedId = null;
        mExpectedName = null;
        mExpectedEmail = null;
        mExpectedToken = null;
        mExpectedKey = null;
    }

    @Test
    public void testOnSignIn() {        
        SharedPreferences prefs = RuntimeEnvironment.application.getSharedPreferences(
                UserInfo.PREFERENCES, Context.MODE_PRIVATE);

        UserInfo.onSignIn(mExpectedId, mExpectedName, mExpectedEmail, mExpectedKey, mExpectedToken);
        assertThat(mExpectedKey, equalTo(UserInfo.getSessionKey()));

        assertThat(mExpectedToken, equalTo(UserInfo.getSessionToken()));
        assertThat(mExpectedId, equalTo(UserInfo.getUserId()));
        assertThat(mExpectedEmail, equalTo(UserInfo.getUserEmail()));
        assertThat(mExpectedName, equalTo(UserInfo.getUserName()));

        assertThat(6, equalTo(prefs.getAll().size()));
    }

    @Test
    public void testOnSignOut() {
        UserInfo.onSignIn(mExpectedId, mExpectedName, mExpectedEmail, mExpectedKey, mExpectedToken);
        // Set Over21 to make sure we unset this value on Signout
        UserInfo.setIsOver21(true);

        UserInfo.onSignOut();
        SharedPreferences prefs = RuntimeEnvironment.application.getSharedPreferences(
                UserInfo.PREFERENCES, Context.MODE_PRIVATE);
        assertThat(UserInfo.getSessionKey(), nullValue());
        assertThat(UserInfo.getSessionToken(), nullValue());
        assertThat(UserInfo.isOver21Set(), is(false));
        assertThat(prefs.getAll().size(), equalTo(0));
    }

    @Test
    public void testIsSignedIn() {
        assertThat(UserInfo.isSignedIn(), is(false));

        UserInfo.onSignIn(mExpectedId, mExpectedName, mExpectedEmail, mExpectedKey, mExpectedToken);
        assertThat(UserInfo.isSignedIn(), is(true));

        UserInfo.onSignOut();
        assertThat(UserInfo.isSignedIn(), is(false));
    }

    @Test
    public void testIsOver21Set() {
        assertThat(UserInfo.isOver21Set(), is(false));
        UserInfo.setIsOver21(false);
        assertThat(UserInfo.isOver21Set(), is(true));
    }
}

Информация о пользователе:

App — это подкласс Application, а App.getInstance() — синглтон.

private static final SharedPreferences PREFS = App.getInstance()
        .getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE);

public static void onSignIn(String userId, String fullName, String email, String sessionKey,
        String sessionToken) {
    SharedPreferences.Editor editor = PREFS.edit();
    editor.putString(PROPERTY_USER_ID, userId);
    editor.putString(PROPERTY_USER_NAME, fullName);
    editor.putString(PROPERTY_USER_EMAIL, email);
    editor.putString(PROPERTY_SESSION_KEY, sessionKey);
    editor.putString(PROPERTY_SESSION_TOKEN, sessionToken);
    editor.putStringSet(PROPERTY_PENDING_SCANS, new HashSet<String>());

    editor.commit();
}
public static boolean isSignedIn() {
    return getSessionToken() != null;
}

MyRoboRunner: модифицированная версия это, потому что я на Mac, а также потому, что я ориентируюсь на API 22 в своем проекте, но robolectric пока не поддерживает это, поэтому я запускаю свои тесты на API 21.

public class MyRoboRunner extends RobolectricGradleTestRunner {

    public MyRoboRunner(Class<?> klass) throws InitializationError {
        super(klass);
    }

    protected AndroidManifest getAppManifest(Config config) {
        AndroidManifest appManifest = super.getAppManifest(config);
        String moduleRoot = getModuleRootPath(config);

        //can use this line instead dynamic path resolution when AS bug is fix, or use @Config
        //FsFile androidManifestFile = appManifest.getAndroidManifestFile();
        FsFile androidManifestFile = FileFsFile.from(moduleRoot,
                appManifest.getAndroidManifestFile().getPath()
                        .replace("bundles", "manifests/full"));
        FsFile resDirectory = FileFsFile.from(moduleRoot, appManifest.getResDirectory().getPath());
        FsFile assetsDirectory = FileFsFile
                .from(moduleRoot, appManifest.getAssetsDirectory().getPath());
        return new AndroidManifest(androidManifestFile, resDirectory, assetsDirectory) {
            @Override
            public int getTargetSdkVersion() {
                //lollipop bc it's highest that robolectric 3.0 supports
                return Build.VERSION_CODES.LOLLIPOP;
            }

            @Override
            public int getMinSdkVersion() {
                return Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1;
            }
        };
    }

    private String getModuleRootPath(Config config) {
        String moduleRoot = config.constants().getResource("").toString().replace("file:", "");
        return moduleRoot.substring(0, moduleRoot.indexOf("/build"));
    }
}

person waynesford    schedule 05.05.2015    source источник
comment
Какую неудачу вы видите? Не могли бы вы поделиться кодом для тестов?   -  person Eugen Martynov    schedule 06.05.2015
comment
@EugenMartynov Я включил часть кода, который использую для тестов.   -  person waynesford    schedule 06.05.2015


Ответы (2)


Вы храните статические ссылки на SharedPreferences в файле PREFS. Я думаю, ваши тесты будут исправлены, как только вы сбросите их в своем методе tearDown или вообще удалите статические ссылки. Если вы удалите статическую ссылку, то SharedPreferences файл будет очищен самим Robolectric - нет необходимости очищать его снова в tearDown.

Еще один момент, который следует упомянуть: я также использую Mac, и у меня нет проблем с RobolectricGradleTestRunnner. Иногда мне нужно запустить clean перед запуском тестов, но больше ничего.

И еще одна вещь, которую вы упомянули robobolectric-gradle-plugin. Вам это не нужно с Android Studio v1.1+ и плагином android gradle v1.1+

person Eugen Martynov    schedule 07.05.2015

Использование плагина robolectric gradle и установка этого свойства позволяют пройти все мои тесты:

robolectric {
    // Specify max number of processes (default is 1)
    maxParallelForks = 2
}

Но я до сих пор не знаю, почему это повлияет на мои тесты при индивидуальном запуске по сравнению со всеми вместе. Кроме того, кажется, что плагин robolectric gradle больше не нужен, так как я работаю на Android Studio 1.2, но я не мог понять, как заставить его работать без него и вручную установить свойство maxParallelForks.

person waynesford    schedule 05.05.2015