2015-05-01 16 views
47

Ich habe eine Android-App, die Dolch 2 für die Abhängigkeits-Injektion verwendet. Ich benutze auch die neuesten Tools zur Erstellung von Editoren, die eine Build-Variante für Komponententests und eine für Instrumentierungstests ermöglichen. Ich benutze java.util.Random in meiner App, und ich möchte dies zum Testen verspotten. Die Klassen, die ich teste, verwenden keine Android-Sachen, also sind sie nur normale Java-Klassen.Android Unit Tests mit Dolch 2

In meinem Haupt-Code definiere ich ein Component in einer Klasse, die die Application Klasse, aber in den Unit-Tests ein Application Ich bin nicht mit erstreckt. Ich habe versucht, einen Test Module und Component zu definieren, aber Dolch wird Component nicht erzeugen. Ich habe auch versucht, die Component verwenden, die ich in meiner Anwendung definiert und Vertauschen der Module, wenn ich es bauen, aber die Anwendung des Component hat keine inject Methoden für meine Testklassen. Wie kann ich eine Testimplementierung von Random zum Testen bereitstellen?

Hier ist ein Beispielcode:

Anwendung:

public class PipeGameApplication extends Application { 

    private PipeGame pipeGame; 

    @Singleton 
    @Component(modules = PipeGameModule.class) 
    public interface PipeGame { 
     void inject(BoardFragment boardFragment); 
     void inject(ConveyorFragment conveyorFragment); 
    } 

    @Override 
    public void onCreate() { 
     super.onCreate(); 
     pipeGame = DaggerPipeGameApplication_PipeGame.create(); 
    } 

    public PipeGame component() { 
     return pipeGame; 
    } 
} 

Modul:

@Module 
public class PipeGameModule { 

    @Provides 
    @Singleton 
    Random provideRandom() { 
     return new Random(); 
    } 
} 

Basisklasse für Tests:

public class BaseModelTest { 

    PipeGameTest pipeGameTest; 

    @Singleton 
    @Component(modules = PipeGameTestModule.class) 
    public interface PipeGameTest { 
     void inject(BoardModelTest boardModelTest); 
     void inject(ConveyorModelTest conveyorModelTest); 
    } 

    @Before 
    public void setUp() { 
     pipeGameTest = DaggerBaseModelTest_PipeGameTest.create(); // Doesn't work 
    } 

    public PipeGameTest component() { 
     return pipeGameTest; 
    } 
} 

oder:

public class BaseModelTest { 

    PipeGameApplication.PipeGame pipeGameTest; 

    // This works if I make the test module extend 
    // the prod module, but it can't inject my test classes 
    @Before 
    public void setUp() { 
     pipeGameTest = DaggerPipeGameApplication_PipeGame.builder().pipeGameModule(new PipeGameModuleTest()).build(); 
    } 

    public PipeGameApplication.PipeGame component() { 
     return pipeGameTest; 
    } 
} 

Testmodul:

@Module 
public class PipeGameTestModule { 

    @Provides 
    @Singleton 
    Random provideRandom() { 
     return mock(Random.class); 
    } 
} 
+0

Dies wird helfen, https://stackoverflow.com/questions/26939340/how-do-you-override-a-module-dependency-in-a-unit-test-with-dagger-2-0 ? lq = 1 –

+0

Sie sollten vielleicht die Tatsache in Betracht ziehen, dass Dolch nicht für Komponententests empfohlen wird: https://google.github.io/dagger/testing.html –

Antwort

2

Meiner Meinung nach dieses Problem, indem man es aus einem anderen Blickwinkel nähern können. Sie werden leicht in der Lage sein, Ihre Klasse zu testen, indem Sie nicht auf Dagger für die zu testende Bauklasse mit seinen gespotteten Abhängigkeiten angewiesen sind.

Was ich meine, ist zu sagen, dass Sie in dem Testaufbau können:

  • Mock die Abhängigkeiten der Klasse unter Test
  • die Klasse im Test Construct manuell verspottet Abhängigkeiten mit

Wir müssen nicht testen, ob Abhängigkeiten korrekt eingefügt werden, da Dagger die Korrektheit des Abhängigkeitsgraphen während der Kompilierung überprüft. Daher werden solche Fehler durch einen Fehler beim Kompilieren gemeldet. Aus diesem Grund sollte die manuelle Erstellung der zu testenden Klasse in der Setup-Methode akzeptabel sein.

Code-Beispiel, wo die Abhängigkeit injiziert wird in der Klasse unter Test unter Verwendung Konstruktor:

public class BoardModelTest { 

    private BoardModel boardModel; 
    private Random random; 

    @Before 
    public void setUp() { 
    random = mock(Random.class); 
    boardModel = new BoardModel(random); 
    } 

    @Test 
    ... 
} 

public class BoardModel { 
    private Random random; 

    @Inject 
    public BoardModel(Random random) { 
    this.random = random; 
    } 

    ... 
} 

Code-Beispiel, wo die Abhängigkeit injiziert wird unter Test in dem Klassenfeld verwendet (im Fall BoardModel von einem Rahmen aufgebaut ist):

public class BoardModelTest { 

    private BoardModel boardModel; 
    private Random random; 

    @Before 
    public void setUp() { 
    random = mock(Random.class); 
    boardModel = new BoardModel(); 
    boardModel.random = random; 
    } 

    @Test 
    ... 
} 

public class BoardModel { 
    @Inject 
    Random random; 

    public BoardModel() {} 

    ... 
} 
+0

Dies wird in meinem Fall nicht wirklich funktionieren, wie ich es derzeit habe viele Abhängigkeiten injiziert, aber das würde sicherlich für einfachere Fälle funktionieren und ich denke definitiv, dass dies der richtige Ansatz ist, wenn Sie es so machen können. – Pikaling

+0

Warum sagst du, dass du ** @ Inject Random random; ** verwenden sollst, wenn du Dolch nicht benutzt? –

1

Wenn Sie dagger2 mit Android verwenden, können Sie App-Varianten verwenden, um Mocking-Ressourcen bereitzustellen.

Sehen Sie hier für eine Demo von Aromen in Mock Tests (ohne Dolch): https://www.youtube.com/watch?v=vdasFFfXKOY

Dieser Code-Basis hat ein Beispiel: https://github.com/googlecodelabs/android-testing

In Ihrem /src/prod/com/IhreFirma/Component .java Sie liefern Ihre Produktionskomponenten.

In Ihrem /src/mock/com/yourcompany/Component.java Sie bieten Ihre spöttische Komponenten.

Auf diese Weise können Sie erstellen, baut der App mit oder ohne spöttisch. Es ermöglicht auch parallele Entwicklung (Back-End von einem Team, Front-End-App von einem anderen Team), Sie können spotten, bis API-Methoden verfügbar sind.

Wie meine gradle nachgucken (seine ein Makefile):

install_mock: 
    ./gradlew installMockDebug 

install: 
    ./gradlew installProdDebug 

test_unit: 
    ./gradlew testMockDebugUnitTest 

test_integration_mock: 
    ./gradlew connectedMockDebugAndroidTest 

test_integration_prod: 
    ./gradlew connectedProdDebugAndroidTest 
+1

Dieses Repo zeigt keine Dolch 2-Komponenten. Wie verspottest du sie in deinem Projekt? –

6

Sie haben den Nagel auf den Kopf getroffen, indem er sagte:

Anwendung Komponente nicht über inject Methoden für meinen Test Klassen

Um dieses Problem zu umgehen, können wir eine Testversion Ihrer Anwendungsklasse erstellen. Dann können wir eine Testversion Ihres Moduls haben. Und damit alles in einem Test läuft, können wir Robolectric verwenden.

1) Erstellen Sie die Testversion Ihrer Anwendungsklasse

public class TestPipeGameApp extends PipeGameApp { 
    private PipeGameModule pipeGameModule; 

    @Override protected PipeGameModule getPipeGameModule() { 
     if (pipeGameModule == null) { 
      return super.pipeGameModule(); 
     } 
     return pipeGameModule; 
    } 

    public void setPipeGameModule(PipeGameModule pipeGameModule) { 
     this.pipeGameModule = pipeGameModule; 
     initComponent(); 
    }} 

2) Ihre ursprüngliche braucht Anwendungsklasse initcomponent() und pipeGameModule() Methoden

public class PipeGameApp extends Application { 
    protected void initComponent() { 
     DaggerPipeGameComponent.builder() 
      .pipeGameModule(getPipeGameModule()) 
      .build(); 
    } 

    protected PipeGameModule pipeGameModule() { 
     return new PipeGameModule(this); 
    }} 

3 haben) Ihr PipeGameTestModule sollte das Produktionsmodul um einen Konstruktor erweitern:

4) Nun, in Ihrem junit Setup-Test() Methode, um dieses Testmodul auf dem Test-App gesetzt:

@Before 
public void setup() { 
    TestPipeGameApp app = (TestPipeGameApp) RuntimeEnvironment.application; 
    PipeGameTestModule module = new PipeGameTestModule(app); 
    app.setPipeGameModule(module); 
} 

Jetzt können Sie Ihre Testmodul anpassen, wie Sie wollten ursprünglich.

+0

Was meinst du mit 'RuntimeEvironment'? – superuserdo

+0

@superuserdo Es ist ** Robolectric **: http://robolectric.org/using-qualifiers/ –

+0

Das ist toll Igor, danke. Ich brauche nur noch eine Information: Wie injiziert das Test Klasseninstanzen? –

0

Ich hatte eigentlich das gleiche Problem und fand eine sehr einfache Lösung. Dies ist nicht die bestmögliche Lösung, aber ich denke, es wird Ihr Problem lösen.

Erstellen Sie eine ähnliche Klasse in der App-Modul:

public class ActivityTest<T extends ViewModelBase> { 

    @Inject 
    public T vm; 
} 

in Ihrem AppComponent Add Dann:

void inject(ActivityTest<LoginFragmentVM> activityTest); 

Dann werden Sie in der Lage sein, dass in der Testklasse zu injizieren.

public class HelloWorldEspressoTest extends ActivityTest<LoginFragmentVM> { 

    @Rule 
    public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule(MainActivity.class); 

    @Test 
    public void listGoesOverTheFold() throws InterruptedException { 
     App.getComponent().inject(this); 
     vm.email.set("1234"); 
     closeSoftKeyboard(); 
    } 
} 
+0

Was ist ** ViewModelBase **? –