някои тестове на 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