4 votes

Android, Room échoue sur les tests unitaires avec Robolectric

J'essaie de créer des tests pour un modèle de vue.

Le modèle de vue comprend une instanciation de base de données qui appelle une base de données

CallRoomDatabase db = CallRoomDatabase.getDatabase(application);

où getDatabase prend une instance de Dao()

  @Database(entities = {CallEntity.class}, version = 1)
 public abstract class CallRoomDatabase extends RoomDatabase {

public abstract CDao cDao();

// marking the instance as volatile to ensure atomic access to the variable
private static volatile CRoomDatabase INSTANCE;

public static CRoomDatabase getDatabase(final Context context) {
    if (INSTANCE == null) {
        synchronized (CallRoomDatabase.class) {
            if (INSTANCE == null) {
                INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                        CRoomDatabase.class, "database")
                        // Wipes and rebuilds instead of migrating if no Migration object.
                        // Migration is not part of this codelab.
                        .fallbackToDestructiveMigration()
                        .addCallback(sRoomDatabaseCallback)
                        .build();
            }
        }
    }
    return INSTANCE;
}

Cependant, lorsque j'essaie d'instancier le modèle dans un test, j'obtiens une erreur sur .build() au-dessus de

  java.lang.NullPointerException
at androidx.room.Room.getGeneratedImplementation(Room.java:79)
at androidx.room.RoomDatabase$Builder.build(RoomDatabase.java:952)
at com.s.o.dbutils.CRoomDatabase.getDatabase(CRoomDatabase.java:32)
at com.s.o.viewmodels.CViewModelTest.checkForNuTest(CViewModelTest.kt:66)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.powermock.modules.junit4.rule.PowerMockStatement$1.run(PowerMockRule.java:83)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)

Sur RoomDatabase.java

            T db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);

Sur Room.java klass es null

        static <T, C> T getGeneratedImplementation(Class<C> klass, String suffix) {

J'ai essayé de ne pas utiliser le modèle du tout lors du test, juste d'instancier la base de données avec

          val db = CRoomDatabase.getDatabase(ApplicationProvider.getApplicationContext())

Il se casse toujours au même .build()

Une idée pour contourner cette exception ?

Nous n'avons pas vraiment besoin de tester la base de données d'une manière ou d'une autre, seulement certaines fonctions incluses dans ce modèle, donc un moyen d'éviter l'erreur serait suffisant.

4voto

JA B Points 28

La pièce doit être testée en tant que Test d'instrumentation Android et non un test JUnit.

Mise à jour :

Utilice mockito pour simuler la base de données et les référentiels.

Utilise des méthodes mockito comme :

  • @Mock - Annotation de simulacre pour simuler les variables globales et locales.
  • simuler - Initie une simulation d'une classe en ligne.
  • quand - configurer le comportement de retour pour mock.
  • vérifier - Assurer les interactions sur les mocks et leurs méthodes.
  • temps - Utilisé avec verify pour affirmer le nombre d'appels à un mock et à sa méthode.
  • cualquier - Un argument matcher pour affirmer les valeurs fournies aux mocks.

En utilisant Mockito, par exemple. Je ne sais pas comment fonctionne votre code, j'ai fait quelques suppositions comme l'existence d'une classe Call.class et les mocks dont vous auriez besoin :

@Mock private CallRoomDatabase database;
@Mock private CRoomDatabase cRoomDatabase;
@Mock private CDao cDao;

private MyClass myClass;

@Before 
public void setUp() {
    // initiate all globally defined mocks annotated with @Mock
    initMocks(this);

    // Setup our expected behaviour from the mock
    when(database.getDatabase()).thenReturn(cRoomDatabase);
    myClass = new myClass(database, cDao);
}

@Test 
public void givenSomeTest_whenCallingGetCall_thenInsertNewCall() {
    // Setup our expected behaviour from the mock
    when(cDao.getCall()).thenReturn(mock(Call.class));

    // do test

    // assert mock interaction with arguments and number of expected calls
    verify(cDao, times(1)).insertCall(any(mock(Call.class)))
}

0voto

Destroyer Points 716

Pour tester le programme de manière isolée de la base de données, je recommande de mettre en place 2 référentiels : 1 référentiel pour le projet, 1 pour les tests et une interface commune pour eux. Vous pouvez implémenter ceci en utilisant Dagger 2.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X