Dagger 2.11 - Injecting different networkAPI implementations in acceptance tests - java

I have a simple app. It uses a network API (Volley). It uses Dagger 2.11 (using AndroidInjector) for dependency inversion. The NetworkAPI is injected in the MainActivity. For the acceptance tests I use my own TestRunner, extended from AndroidJUnitRunner.
To fake the real Network API, I use an ImmediateResponseNetwork object. This 'network' responses immediately. The instance is provided by a TestNetworkApiModule and declared in the module list of the TestAppComponent. See parts of the source code below.
At the times, before I used Dagger, I used the ImmediateResponseNetwork to define different responses of the networkRequest, dependend on the test. Which seem not be possible now, because the network is hidden in the activity under test. And after creation of the network I can't access it from the outside to prepare it.
I also can't test a slower network connection. That is why I would like to at least to inject another fake slow-response-network object and I would like to have the ability to prepare the response via ImmediateResponseNetwork.
How can I do this?
An example test I would like to write is:
Testing, that the loading indicator is displayed, while an network request is running.
I have no idea how to implement this now. It looks like, that DI is in the way for easier testing, instead of the other way around.
I guess, there is something missing in my thinking.
The source code on which the question is based upon: https://github.com/Minsky/SO_Question_AndroidInjectionInTestsWithDagger
Sample Test, I want to do:
#Rule
public ActivityTestRule<MainActivity> mainActivityTestRule = new ActivityTestRule<>(MainActivity.class, true, false);
public void afterStartLoadingPleaseWaitInfoIsDisplayed() throws Exception {
MainActivityTestRule.launchActivity(new Intent());
onView(withId(R.id.activity_loading_indicator)).check(matches(isDisplayed()));
}
My relevant test setup classes:
#Module
public class TestNetworkApiModule extends NetworkApiModule {
#Provides
static NetworkApi provideNetworkApi(Context context) {
return new ImmediateResponseNetwork();
}
}
#Singleton
#Component(modules = {
AppModule.class,
TestNetworkApiModule.class,
ActivityBindingModule.class,
AndroidSupportInjectionModule.class
})
public interface TestAppComponent extends AppComponent {
void inject(TestMyApplication app);
#Override
void inject(DaggerApplication instance);
#Component.Builder
interface Builder {
#BindsInstance
TestAppComponent.Builder application(Application application);
TestAppComponent build();
}
}
public class TestMyApplication extends DaggerApplication {
#Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
TestAppComponent appComponent = DaggerTestAppComponent.builder().application(this).build();
appComponent.inject(this);
return appComponent;
}
}
The activity where the networkAPI is injected:
public class MainActivity extends AppCompatActivity {
private TextView responseView;
private boolean progressBarVisible;
#Inject
NetworkApi networkApi;
...
}

Related

Dagger dependency injection to service fails with "contactServiceModule must be set at DaggerAppComponent$Builder.build(DaggerAppComponent.java)"

I am using dagger for dependency injection, I apologize for not diving into deep and getting my hands on :)
I am using two modules(ApplicationModule, ContactServiceModule), with a component (AppComponent). I have initialized the Dagger in App.java, and I am trying to inject dependencies to a backgroud Service.
Here is the code snippet.
1)ApplicationModule.java
#Module
public class ApplicationModule {
private final App mApp;
private Service contactService;
public ApplicationModule(App app) {
mApp = app;
}
#Provides
#Singleton
public ContactModel contactModel(SQLiteDatabase database) {
return new ContactModel(mApp, database);
}
#Provides
#Singleton
public Context appContext() {
return mApp;
}
#Provides
#Singleton
public ContactController contactController() {
return new ContactController(mApp.getAppComponent());
}
//.....Some more code
}
2)ContactServiceModule.java
#Module
public class ContactServiceModule {
ContactService contactService;
public ContactServiceModule(ContactService contactService){
this.contactService =contactService;
}
#Provides
#Singleton
ContactService provideContactService() {
return this.contactService;
}
}
3)AppComponent.java
#Singleton
#Component(modules = {ApplicationModule.class,ContactServiceModule.class})
public interface AppComponent {
ContactController contactController();
Context appContext();
//...Some code
void inject(ContactController contactController);
//...Some code
void inject(ContactService contactService);
}
4)App.java
public class App extends Application {
private AppComponent mAppComponent;
#Override
public void onCreate() {
super.onCreate();
FlowManager.init(this);
mAppComponent = DaggerAppComponent.builder()
.applicationModule(new ApplicationModule(this))
.build();
}
public AppComponent getAppComponent() {
return mAppComponent;
}
}
5) ContactService.java
public class ContactService extends Service {
#Inject
Context appContext;
#Inject
ContactController contactController;
#Override
public void onCreate() {
super.onCreate();
getAppComponent().inject(this);
}
private AppComponent getAppComponent() {
return ((App)getApplication()).getAppComponent();
}
}
It could be an issue of dependencies of UI threads injected into background services. But I do not understand how should I achieve the same dependencies to be injected into services. Or what should be the Ideal way to communicate between application level dependencies and services. I want the service to be running always in the background and listening to various intents and also communicate to other dependencies like eventbus etc.
The error message:
contactServiceModule must be set at DaggerAppComponent$Builder.build(DaggerAppComponent.java)”
means that you have created a module with dependencies in the constructor (ContactServiceModule which has your Service as a parameter in the constructor). It follows that Dagger 2 cannot instantiate that module automatically and it must be instantiated in the builder when you create your component:
mAppComponent = DaggerAppComponent.builder()
.applicationModule(new ApplicationModule(this))
.contactServiceModule(contactService);
.build();
However there is an underlying issue with the architecture you are trying to create. Services, like Activities, are designed to be autonomous and instantiated by the Android OS. Much in the same way you wouldn't directly pass as an Activity as a dependency to another Activity, you shouldn't make a Service a dependency. Instead you need to use Intent, service binding, or EventBus events to communicate with the service. The Android Service documentation covers this and you should read it carefully before proceeding.

Understanding the Dagger2 Flow (Sample provided)

I'm trying to wrap my head around the use of Dagger 2 in my project with Retrofit, RxJava, MVP implementation. However, I'm starting small by reading guides and watching videos and just when I thought I understood how it works, it appears I don't. Here is the sample I'm trying to understand.
Module:
#Module
public class AppModule {
private App app;
public AppModule(App app) {
this.app = app;
}
#Provides #Singleton public SharedPreferences provideSharedPreferences() {
return PreferenceManager.getDefaultSharedPreferences(app);
}
#Provides #Singleton public HelloModel provideHelloModel(SchedulerProvider schedulerProvider,
HelloDiskCache helloDiskCache, HelloService helloService, Clock clock) {
return new HelloModel(schedulerProvider, helloDiskCache, helloService, clock);
}
#Provides public HelloDiskCache provideHelloDiskCache(SharedPreferences prefs) {
return new HelloDiskCache(prefs);
}
#Provides public HelloService provideHelloService() {
return new HelloService();
}
#Provides public SchedulerProvider provideSchedulerProvider() {
return SchedulerProvider.DEFAULT;
}
#Provides public Clock provideClock() {
return Clock.REAL;
}
#Provides #Nullable public LayoutInflaterFactory provideLayoutInflaterFactory() {
return null;
}
Component
#Component(
modules = AppModule.class
)
#Singleton
public interface AppComponent {
HelloModel getHelloModel();
HelloDiskCache getHelloDiskCache();
MainActivity inject(MainActivity activity);
HelloFragment inject(HelloFragment fragment);
}
In the fragment, presenter is injected (where is this coming from?)
#Inject HelloPresenter presenter;
And in the presenter, there is a constructor injection
#Inject HelloPresenter(HelloModel helloModel) {
this.model = helloModel;
}
So, how come we can inject a presenter in the fragment, and why can we inject in the presenter? The answer does not have to be very elaborate I just feel stupid because I can't trace where it is coming from.
#Inject-annotated constructor is an alternative to a #Provides-annotated method for a dependency when there's not much configuration to do.
Since HelloPresenter has such constructor, Dagger discovers it automatically and is able to inject this dependency or use it to construct and provide other objects in the graph.
A class with an #Inject-annotated constructor can itself be annotated with a scope annotation (e.g., #Singleton). In this case, only components with the matching scope will be able to see it.
In general, this type of providing dependencies is less verbose, but not always you can use it (e.g., Activity, SharedPreferences). In such cases you have to go with a #Module and a #Provides method.
You can look at this sample where Dagger2 used with MVP Architecture
Dagger2 Android with MVP

Use Guice to create components to use with ThreadWeaver

The application I have been working on has been getting more and more complicated, and it's gotten to the point where I have been running into the same problems over and over again with concurrency. It no longer made any sense to solve the same problems and not have any regression tests.
That's when I found ThreadWeaver. It was really nice for some simple concurrency cases I cooked up, but I started to get frustrated when trying to do some more complicated cases with my production code. Specifically, when injecting components using Guice.
I've had a bit of a hard time understanding the implications of the way ThreadWeaver runs tests, and looked for any mention of Guice or DI in the wiki documents, but with no luck.
Is Guice compatible with ThreadWeaver?
Here is my test
#Test
public void concurrency_test() {
AnnotatedTestRunner runner = new AnnotatedTestRunner();
runner.runTests(OPYLWeaverImpl.class, OPYLSurrogateTranscodingService.class);
}
Here is my test implementation
public class OPYLWeaverImpl extends WeaverFixtureBase {
#Inject private TaskExecutor taskExecutor;
#Inject private Serializer serializer;
#Inject private CountingObjectFileMarshaller liveFileMarshaller;
#Inject private GraphModel graphModel;
#Inject private CountingModelUpdaterService updaterService;
#Inject private BabelCompiler babelCompiler;
#Inject private EventBus eventBus;
OPYLSurrogateTranscodingService service;
private Path testPath;
#ThreadedBefore
public void before() {
service = new OPYLSurrogateTranscodingService(eventBus, taskExecutor, serializer, liveFileMarshaller,
() -> new OPYLSurrogateTranscodingService.Importer(graphModel, babelCompiler, updaterService, eventBus),
() -> new OPYLSurrogateTranscodingService.Validator(eventBus, babelCompiler),
() -> new OPYLSurrogateTranscodingService.Exporter(graphModel, updaterService));
}
#ThreadedMain
public void mainThread() {
testPath = FilePathOf.OASIS.resolve("Samples/fake-powershell-unit-test.opyl");
service.applyToExistingGraphModel(testPath);
}
#ThreadedSecondary
public void secondaryThread() {
}
#ThreadedAfter
public void after() {
}
And the WeaverFixtureBase
public class WeaverFixtureBase {
#Inject protected CountingEventBus eventBus;
#Before public final void setupComponents() {
Injector injector = Guice.createInjector(new WeaverTestingEnvironmentModule(CommonSerializationBootstrapper.class));
injector.getMembersInjector((Class) this.getClass()).injectMembers(this);
}
private class WeaverTestingEnvironmentModule extends AbstractModule {
private final Class<? extends SerializationBootstrapper> serializationBootstrapper;
public WeaverTestingEnvironmentModule(Class<? extends SerializationBootstrapper> serializationConfiguration) {
serializationBootstrapper = serializationConfiguration;
}
#Override protected void configure() {
bind(TaskExecutor.class).to(FakeSerialTaskExecutor.class);
bind(SerializationBootstrapper.class).to(serializationBootstrapper);
bind(ModelUpdaterService.class).toInstance(new CountingModelUpdaterService());
bindFactory(StaticSerializationConfiguration.Factory.class);
CountingEventBus localEventBus = new CountingEventBus();
bind(Key.get(EventBus.class, Bindings.GlobalEventBus.class)).toInstance(localEventBus);
bind(Key.get(EventBus.class, Bindings.LocalEventBus.class)).toInstance(localEventBus);
bind(CountingEventBus.class).toInstance(localEventBus);
bind(EventBus.class).toInstance(localEventBus);
}
#Provides
#Singleton
public GraphModel getGraphModel(EventBus eventBus, Serializer serializer) {
return MockitoUtilities.createMockAsInterceptorTo(new GraphModel(eventBus, serializer));
}
}
But when the classloader loads OPYLWeaverImpl, none of the Guice stuff goes off and I get a big pile of nulls.
I feel like this is one of those "missing-something-really-simple" kind of scenarios. Sorry if it is!
The above comment is right. Thread-weaver is fully agnostic of JUnit. Thread weaver is its own runner that executes a test case respecting its own annotations. You must not use any JUnit-specific annotation within a Thread Weaver test.
Other than that, Thread Weaver does not need any compatibility for a specific framework. It manipulates Java byte code and loads that manipulated code using aeperate class loaders.
Finally, a Thread Weaver test without any secondary test does not make any sense. Thread weaver works by interleaving seperate execution paths. Without a second thread, Thread Weaver only steps through a single thread without adding any value.

Android Unit Tests with Dagger 2

I have an Android app that uses Dagger 2 for dependency injection. I am also using the latest gradle build tools that allow a build variant for unit testing and one for instrumentation tests. I am using java.util.Random in my app, and I want to mock this for testing. The classes I'm testing don't use any Android stuff, so they're just regular java classes.
In my main code I define a Component in a class that extends the Application class, but in the unit tests I'm not using an Application. I tried defining a test Module and Component, but Dagger won't generate the Component. I have also tried using the Component that I defined in my application and swapping the Module when I build it, but the application's Component doesn't have inject methods for my test classes. How can I provide a mock implementation of Random for testing?
Here's some sample code:
Application:
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;
}
}
Module:
#Module
public class PipeGameModule {
#Provides
#Singleton
Random provideRandom() {
return new Random();
}
}
Base class for 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;
}
}
or:
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;
}
}
Test Module:
#Module
public class PipeGameTestModule {
#Provides
#Singleton
Random provideRandom() {
return mock(Random.class);
}
}
This is currently impossible with Dagger 2 (as of v2.0.0) without some workarounds. You can read about it here.
More about possible workarounds:
How do you override a module/dependency in a unit test with Dagger 2.0?
Creating test dependencies when using Dagger2
You have hit the nail on the head by saying:
application's Component doesn't have inject methods for my test classes
So, to get around this problem we can make a test version of your Application class. Then we can have a test version of your module. And to make it all run in a test, we can use Robolectric.
1) Create the test version of your Application class
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) Your original Application class needs to have initComponent() and pipeGameModule() methods
public class PipeGameApp extends Application {
protected void initComponent() {
DaggerPipeGameComponent.builder()
.pipeGameModule(getPipeGameModule())
.build();
}
protected PipeGameModule pipeGameModule() {
return new PipeGameModule(this);
}}
3) Your PipeGameTestModule should extend the production module with a constructor:
public class PipeGameTestModule extends PipeGameModule {
public PipeGameTestModule(Application app) {
super(app);
}}
4) Now, in your junit test's setup() method, set this test module on your test app:
#Before
public void setup() {
TestPipeGameApp app = (TestPipeGameApp) RuntimeEnvironment.application;
PipeGameTestModule module = new PipeGameTestModule(app);
app.setPipeGameModule(module);
}
Now you can customize your test module how you originally wanted.
In my opinion you can approach this problem by looking at it from a different angle. You will easily be able to unit test your class by not depending upon Dagger for construction class under test with its mocked dependencies injected into it.
What I mean to say is that in the test setup you can:
Mock the dependencies of the class under test
Construct the class under test manually using the mocked dependencies
We don't need to test whether dependencies are getting injected correctly as Dagger verifies the correctness of the dependency graph during compilation. So any such errors will be reported by failure of compilation. And that is why manual creation of class under test in the setup method should be acceptable.
Code example where dependency is injected using constructor in the class under test:
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 example where dependency is injected using field in the class under test (in case BoardModel is constructed by a framework):
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() {}
...
}
If you are using dagger2 with Android, you can use app flavours for providing mocking resources.
See here for a demo of flavours in mock testing(without dagger):
https://www.youtube.com/watch?v=vdasFFfXKOY
This codebase has an example:
https://github.com/googlecodelabs/android-testing
In your /src/prod/com/yourcompany/Component.java
you provide your production components.
In your /src/mock/com/yourcompany/Component.java
you provide your mocking components.
This allows you create builds of your app with or without mocking.
It also allows parallel development (backend by one team, frontend app by another team), you can mock until api methods are avilable.
How my gradle commands look (its a Makefile):
install_mock:
./gradlew installMockDebug
install:
./gradlew installProdDebug
test_unit:
./gradlew testMockDebugUnitTest
test_integration_mock:
./gradlew connectedMockDebugAndroidTest
test_integration_prod:
./gradlew connectedProdDebugAndroidTest
I actually had the same issue and found a very simple solution.
This is not the best possible solution I think but it will solve your problem.
Create a similar class in your app module:
public class ActivityTest<T extends ViewModelBase> {
#Inject
public T vm;
}
Then, in your AppComponent add:
void inject(ActivityTest<LoginFragmentVM> activityTest);
Then you will be able to inject that in your test class.
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();
}
}

COnstructor Injection with roboguice enters inifinite loop

HI I come from a background C#/.NET and have learned to work with a bit of Android.Now I am wana start to build a small app for fun and I tought to learn an IoC framework.After a bit of googeling I found roboguice.But I can not figure out how to integrate it.On .
NET I have worked with Ninject and Unity , and am looking to create a similar form of constructor injection that I got from those frameworks.
Here is what I have so far and what I think I have figured out:
This class represent the app bootstrapper and here is where I will register my dependency config class:
public class IOCApplication extends Application{
#Override
public void onCreate(){
super.onCreate();
RoboGuice.setBaseApplicationInjector(this, RoboGuice.DEFAULT_STAGE, RoboGuice.newDefaultRoboModule(this), new IOCModule());
}
}
This is the Dependency config class:
public class IOCModule implements Module{
#Override
public void configure(com.google.inject.Binder binder) {
binder.bind(ITest.class).to(Test.class);
}
}
In my AndroidManifest I have added this:
<application ... android:name="com.example.project2.IOCApplication">
This part I do not realy understad why I had to add but I am thinking it's something to tell Android that IOCApplication should be isntantiated first.
This is the clas my MainActivily class and I have added a constructor for it:
public ITest test;
public MainActivity(ITest test){
this.test = test;
}
When I try to runthis on my android device it kind of looks like the app is entering an infinite loop and I do not think ITest get's instantiated.
What am I doing wrong?
One thing to know with Android is that you do not instantiate your own activities, it is the system which does.
Because of this, you cannot use constructor injection with activities.
However, you can use attribute injection, which is cleaner IMO.
Extending the RoboActivity class is the easiest way to use injection with Activity.
RoboGuice provides similar classes for other Android components (RoboFragment, RoboService, etc.)
public class MyActivity extends RoboActivity {
#Inject
ITest test;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// You can now use your test instance
}
}
All attribute with the #Inject will be instantiated after super.onCreate(savedInstanceState); is called.
With POJO (Plain Old Java Object), you have more options:
Attribute injection
public class Test {
#Inject
private Service1 service1;
#Inject
private Service2 service2;
public Test() {
}
}
Constructor injection
public class Test {
private Service1 service1;
private Service2 service2;
#Inject
public Test(Service1 service1, Service2 service2) {
this.service1 = service1;
this.service2 = service2;
}
}
Note that your constructor must have the #Inject annotation if it has arguments.
You need this line <application ... android:name="com.example.project2.IOCApplication"> to tell the system that you are using an extended Application class. Without it, Android will use the base class.
I encourage you to read the official documentation for more information.

Categories