2016-07-11 13 views
1

Also arbeite ich an diesem kleinen Projekt, das Dolch 2 für Abhängigkeitsinjektion und Realm als Datenbank verwendet.Komponententest Realm + Dolch 2 mit Robolectric & Mockito

Ich teste es mit Robolectric und Mockito (mit Powermock). Aus früheren Forschungen (und sehr viel Schmerz) habe ich gemerkt, dass das Testen von Realm ziemlich mühsam ist, aber in der Vergangenheit wurde es getan here.

Jetzt hat mein Projekt eine sehr ähnliche Konfiguration und Struktur wie oben beschrieben.

Wenn ich meine Unit-Tests laufen, alle von ihnen passieren mit einer Ausnahme, die mir eine sehr kryptische Nachricht gibt, die wie folgt aussieht:

java.lang.NullPointerException 
at org.robolectric.internal.ShadowExtractor.extract(ShadowExtractor.java:5) 
at org.robolectric.Shadows.shadowOf(Shadows.java:1190) 
at org.robolectric.shadows.CoreShadowsAdapter.getMainLooper(CoreShadowsAdapter.java:37) 
at org.robolectric.util.ComponentController.<init>(ComponentController.java:31) 
at org.robolectric.util.ComponentController.<init>(ComponentController.java:23) 
at org.robolectric.util.ActivityController.<init>(ActivityController.java:40) 
at org.robolectric.util.ActivityController.of(ActivityController.java:32) 
at org.robolectric.Robolectric.buildActivity(Robolectric.java:82) 
at org.robolectric.Robolectric.buildActivity(Robolectric.java:78) 
at org.robolectric.Robolectric.setupActivity(Robolectric.java:86) 
at uk.co.placona.tradesafe.view.EditActivityTest.ActivityShouldNotBeNull(EditActivityTest.java:54) 
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.internal.runners.TestMethod.invoke(TestMethod.java:68) 
at org.powermock.modules.junit4.internal.impl 

Die Codezeile auf den Fehler oben angegebenen ist:

Die Aktivität existiert und wird beim Start mit einem TradeRepository versehen.

Die fragliche Aktivität kann here zusammen mit dem Rest des Codes gefunden werden. Ich habe versucht, dies für etwa 3 Tage jetzt ohne Erfolg zu debuggen. Jeder andere Komponententest, den ich erstelle, funktioniert gut, mit Ausnahme eines Komponententests, der von einer Aktivität verwendet wird, was mich glauben lässt, dass ich etwas wirklich Offensichtliches vermisse.

Wäre glücklich, hier Fragen zu klären. Vielen Dank!

+0

Laufen Sie es mit 'PowerMock'? –

+0

Ja, wie Sie hier sehen können: https://github.com/mplacona/trade-safe/blob/master/app/src/test/java/uk/co/placona/tradesafe/view/EditActivityTest.java –

+0

Yeah , schon geprüft.Es ist immer schwierig mit 'PowerMock' –

Antwort

2

statisch ist böse, powermock ist böse :).

Ich denke, Sie sollten Ihre Klasse Injector loswerden. Sie benötigen es nicht, weil Sie während der Lebensdauer Ihrer Anwendung nur ein Objekt mit der Bezeichnung "CustomApplication" haben.

Sie sollten Ihren Code wie folgt ändern:

In Custom.Java ist die Anwendungskomponente erstellt, in einem Feld Variable gesetzt, und injizierte

private ApplicationComponent applicationComponent; 

public void setup(){ 
     getOrCreateApplicationComponent().inject(this); 
     databaseRealm.setup(); 
     stethoDebug.setup(this); 
    } 

public ApplicationComponent getOrCreateApplicationComponent() { 
     if (applicationComponent == null) { 
      applicationComponent = DaggerApplicationComponent.builder() 
        .applicationContextModule(new ApplicationContextModule(this)) 
        .repositoryModule(new RepositoryModule()) 
        .build(); 
     } 

     return applicationComponent; 
    } 

In den Verfahren onCreate von CreateActivity, EditActivity und MainActivity wird Injector mit

((CustomApplication) getApplication()) 
      .getOrCreateApplicationComponent() 
      .inject(this); 

In RepositoryModule ersetzt werden wir 2 verwenden Dagger Abhängigkeiten in Konstrukteuren zu injizieren, so dass wir den Kontext injizieren müssen in Da nicht manuell und die DatabaseRealm

@Provides 
@Singleton 
public TradeRepository provideTradeRepository(DatabaseRealm databaseRealm) { 
    return new TradeRepositoryImpl(databaseRealm); 
} 

@Provides 
@Singleton 
public DatabaseRealm provideDatabaseRealm(Context context) { 
    return new DatabaseRealm(context); 
} 

dann tabaseRealm wir einen Konstruktor mit Kontext als Parameter

Context mContext; 

RealmConfiguration realmConfiguration; 

public DatabaseRealm(Context context) { 
    mContext = context; 
} 

und gleichen in TradeRepositoryImpl, ein Konstruktor mit databaseRealm hinzugefügt

DatabaseRealm databaseRealm; 

public TradeRepositoryImpl(DatabaseRealm databaseRealm) { 
    this.databaseRealm = databaseRealm; 
} 

ist

Für RepositoryTestModule, fügen wir databaseRealm als Parameter:

@Provides 
@Singleton 
public TradeRepository provideTradeRepository(DatabaseRealm databaseRealm) { 
    return isMocked ? mock(TradeRepository.class) : new TradeRepositoryImpl(databaseRealm); 
} 

In Ihrer TestCustomApplication überschreiben wir die getOrCreateApplicationComponent

@Override 
    public ApplicationComponent getOrCreateApplicationComponent() { 
     return DaggerApplicationComponentTest.builder() 
       .applicationContextModuleTest(new ApplicationContextModuleTest()) 
       .repositoryModuleTest(new RepositoryModuleTest(false)) 
       .build(); 
    } 

nun für jede Ihrer Tests, die wir führen sie mit RobolectricGradleTestRunner und fügen TestCustomApplication.class als Anwendungs ​​Tag

@RunWith(RobolectricGradleTestRunner.class) 
@Config(constants = BuildConfig.class, sdk = 21, application = TestCustomApplication.class) 

wenn wir Abhängigkeiten in unseren Tests injizieren müssen wir so injizieren:

@Before 
public void setupDagger() { 
    DaggerApplicationComponentTest.builder() 
      .applicationContextModuleTest(new ApplicationContextModuleTest()) 
      .repositoryModuleTest(new RepositoryModuleTest(false)) 
      .build().inject(this); 
} 

Wir haben noch eine Nullpointer in unserer EditActivityTest weil diese Zeile:

loadTrade(intent.getExtras().getString("ID")); 

Entweder Sie überprüfen, ob die Absicht nicht null ist oder Sie geben eine in Ihrem Test an.

+0

Ich erstelle normalerweise 'Test ', das automatisch von Robolectric geladen wird, um 'CustomTestApplication' zu vermeiden und Logik zu duplizieren . Aber könnte die super gute Idee sein. Ich werde meinen Ansatz überdenken –

+0

Wooow! Du hast mich gerade zur Schule gebracht, Steve! Vielen Dank dafür. Wenn man den Code liest, macht das viel mehr Sinn und sieht viel sauberer aus! Und ja .. statisch .. erzähl mir davon! Ich wünschte, es gäbe eine Möglichkeit für mich, diese Antwort ein paar Mal zu wiederholen! So eine großartige und vollständige Antwort! Vielen Dank für die Zeit, ich schätze es wirklich! –

2

Mit Blick auf Ihre Testklasse, sehe ich nicht, dass Sie Robolectric Testläufer verwenden.

Sie müssen RobolectricGradleTestRunner oder RobolectricTestRunner verwenden Robolecric Funktionalität zum Laden manifestiert, Parsing Ressourcen auslösen, Hauptgreifer zu schaffen usw.

Wenn Sie sie nicht verwenden, haben Sie wahrscheinlich es mit eigenem Code im Setup erzielen können, aber es ist nicht der übliche Weg, und ich bin nicht sicher, dass viele Leute hier Ihnen erklären können, wie Sie es erreichen können.

Ebenso Robolectric und PowerMock modifizieren Java ClassLoader beides. Deshalb ist es so schwer (vielleicht unmöglich), sie zusammenzubringen. So überprüfen Sie @Steve Antwort, wie Sie Ihren Code ändern, um PowerMock Notwendigkeit für Ihren Test zu entfernen.

+0

Danke Eugen, schätze deine Antwort hier wirklich. Ich habe diesen Code in vielerlei Hinsicht verändert, ich habe den Runner irgendwann mal verändert und habe es nicht einmal gemerkt. Wissen Sie, wenn Sie anfangen, alles nach Lust und Laune zu verändern, um zu sehen, ob etwas einen Unterschied macht? Das war ich gestern :-). Wirklich schätzen Sie Ihre Kommentare und Vorschläge! –

+0

Gern geschehen! Danke für den Kommentar und ein Lob an @ steve-c –

Verwandte Themen