I'm using Dagger 2 in Android Studio, but when try to create Component through DaggerMyComponent.builder().myModule(new MyModule()).build(), I always see the word "builder()" in red and it says "cannot resolve symbol builder".
I must say that I've cleaned and builted many times the project, even commenting the component instance and decommenting it after rebuild.
I must also say that I can inspect the DaggerMyComponent.class, and everything seems ok, in fact, I can import that class with no problem.
The example is shared on GithHub, at this link:
https://github.com/alessandroargentieri/LetsDagger2Together
I have two Classes: BClass and AClass which depends on the first one.
I have ABModule.class which is the factory method and ABComponent which is the component interface.
The DaggerABComponent is created into WholeApplication.class which extends Application.
package mawashi.alex.letsdagger2together.Application;
import android.app.Application;
import mawashi.alex.letsdagger2together.DaggerClasses.ABComponent;
import mawashi.alex.letsdagger2together.DaggerClasses.ABModule;
import mawashi.alex.letsdagger2together.DaggerClasses.DaggerABComponent;
public class WholeApplication extends Application {
static ABComponent component;
#Override
public void onCreate() {
super.onCreate();
//here is where builder() is not recognized
component = new DaggerABComponent.builder().aBModule(new ABModule()).build();
}
public static ABComponent getComponent(){
return component;
}
}
Module:
package mawashi.alex.letsdagger2together.DaggerClasses;
import dagger.Module;
import dagger.Provides;
import mawashi.alex.letsdagger2together.Model.AClass;
import mawashi.alex.letsdagger2together.Model.BClass;
#Module
public class ABModule {
#Provides
public BClass provideBClass(){
return new BClass("xxx");
}
#Provides
public AClass provideAClass(BClass bClass){
return new AClass(bClass);
}
}
Component:
package mawashi.alex.letsdagger2together.DaggerClasses;
import dagger.Component;
import mawashi.alex.letsdagger2together.MainActivity;
#Component (modules = {ABModule.class})
public interface ABComponent {
public void inject(MainActivity mainActivity);
}
Classes which are to be injected:
package mawashi.alex.letsdagger2together.Model;
public class BClass {
private String xfactor;
public BClass(String xfactor){
this.xfactor = xfactor;
}
public String getXfactor(){
return xfactor;
}
}
package mawashi.alex.letsdagger2together.Model;
public class AClass {
private BClass b;
public AClass(BClass b){
this.b = b;
}
public String getYfactor(){
return "Y-" + b.getXfactor() + "-Y";
}
}
Place where to inject AClass and BClass:
package mawashi.alex.letsdagger2together;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
import javax.inject.Inject;
import mawashi.alex.letsdagger2together.Application.WholeApplication;
import mawashi.alex.letsdagger2together.DaggerClasses.ABModule;
import mawashi.alex.letsdagger2together.DaggerClasses.DaggerABComponent;
import mawashi.alex.letsdagger2together.Model.AClass;
public class MainActivity extends AppCompatActivity {
#Inject AClass a;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WholeApplication.getComponent().inject(this);
Toast.makeText(this, a.getYfactor(),Toast.LENGTH_LONG).show();
}
}
If anyone knows what I mistake it would be very nice to me. Thanks to everybody.
Oh, I've figured it out.
You added this line:
component = new DaggerABComponent.builder().aBModule(new ABModule()).build();
And it should be this line:
component = DaggerABComponent.builder().aBModule(new ABModule()).build();
So just remove new and then it'll work.
All fields with #Inject annotation must be public:
#Inject public AClass a; in your Activity
Related
I'm trying to learn Hilt and am getting the error "Dagger does not support injection into private fields". But the field in question isn't private in the original Kotlin. It's only private when it's converted to Java. Why is this? How can I correct it?
Original Kotlin file:
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.example.vennwithnav.databinding.FragmentLoginBinding
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
#AndroidEntryPoint
class LoginFragment : Fragment() {
#Inject public var profileViewModel: ProfileViewModel? = null
private var binding: FragmentLoginBinding? = null
Java file after conversion (accessed via the error):
package com.example.vennwithnav.ui.profile;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import com.example.vennwithnav.databinding.FragmentLoginBinding;
import dagger.hilt.android.AndroidEntryPoint;
import javax.inject.Inject;
#kotlin.Metadata(mv = {1, 4, 1}, bv = {1, 0, 3}, k = 1, d1 = {"\u0000:\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0005\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0002\b\u0007\u0018\u00002\u00020\u0001B\u0005\u00a2\u0006\u0002\u0010\u0002J\u0006\u0010\u000b\u001a\u00020\fJ&\u0010\r\u001a\u0004\u0018\u00010\u000e2\u0006\u0010\u000f\u001a\u00020\u00102\b\u0010\u0011\u001a\u0004\u0018\u00010\u00122\b\u0010\u0013\u001a\u0004\u0018\u00010\u0014H\u0016J\b\u0010\u0015\u001a\u00020\fH\u0016R\u0010\u0010\u0003\u001a\u0004\u0018\u00010\u0004X\u0082\u000e\u00a2\u0006\u0002\n\u0000R\u001a\u0010\u0005\u001a\u00020\u0006X\u0086\u000e\u00a2\u0006\u000e\n\u0000\u001a\u0004\b\u0007\u0010\b\"\u0004\b\t\u0010\n\u00a8\u0006\u0016"}, d2 = {"Lcom/example/vennwithnav/ui/profile/LoginFragment;", "Landroidx/fragment/app/Fragment;", "()V", "binding", "Lcom/example/vennwithnav/databinding/FragmentLoginBinding;", "profileViewModel", "Lcom/example/vennwithnav/ui/profile/ProfileViewModel;", "getProfileViewModel", "()Lcom/example/vennwithnav/ui/profile/ProfileViewModel;", "setProfileViewModel", "(Lcom/example/vennwithnav/ui/profile/ProfileViewModel;)V", "login", "", "onCreateView", "Landroid/view/View;", "inflater", "Landroid/view/LayoutInflater;", "container", "Landroid/view/ViewGroup;", "savedInstanceState", "Landroid/os/Bundle;", "onDestroyView", "app_debug"})
#dagger.hilt.android.AndroidEntryPoint()
public final class LoginFragment extends androidx.fragment.app.Fragment {
#org.jetbrains.annotations.NotNull()
private com.example.vennwithnav.ui.profile.ProfileViewModel profileViewModel; // this line is causing the error
private com.example.vennwithnav.databinding.FragmentLoginBinding binding;
#org.jetbrains.annotations.NotNull()
public final com.example.vennwithnav.ui.profile.ProfileViewModel getProfileViewModel() {
return null;
}
public final void setProfileViewModel(#org.jetbrains.annotations.NotNull()
com.example.vennwithnav.ui.profile.ProfileViewModel p0) {
}
One more google search found the answer. https://kotlinlang.org/docs/java-to-kotlin-interop.html#instance-fields
Adding #JvmField in front of var fixed the problem.
#Inject #JvmField var profileViewModel: ProfileViewModel? = null
per IR42, this works too, and is the used in the developer.android.com tutorials, so it's probably better.
#Inject lateinit var profileViewModel: ProfileViewModel
I have checked all answers on the stack. But nothing helps. Might be problem in Kotlin + DI
So I got an exception that Eventbus for some reason cant initialize itself in presenter class. By debugger I see that method of initializing executed proper way from onCreate. I use the same code as I use in other classes (Java classes) and other works correctly. Could you please give an advice where might be an issue
Error
Caused by: org.greenrobot.eventbus.EventBusException: Subscriber class com.myapp.mvp.ui.myprofile.MyProfileActivity and its super classes have no public methods with the #Subscribe annotation
at org.greenrobot.eventbus.SubscriberMethodFinder.findSubscriberMethods(SubscriberMethodFinder.java:67)
at org.greenrobot.eventbus.EventBus.register(EventBus.java:140)
at com.myapp.mvp.ui.myprofile.MyProfileActivity.onCreate(MyProfileActivity.kt:67)
at android.app.Activity.performCreate(Activity.java:7981)
at android.app.Activity.performCreate(Activity.java:7970)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1307)
My presenter class
import com.arellomobile.mvp.InjectViewState
import com.arellomobile.mvp.MvpPresenter
import com.myapp.UserProfileItemsList
import com.myapp.eventbus.VisitsEvent
import com.myapp.eventbus.UserEvent
import com.myapp.models.UserDescriptionModel
import com.myapp.mvp.model.interactor.myprofile.MyProfileInteractor
import com.myapp.utils.ActionsCountInfoCallback
import com.myapp.utils.UserCallback
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import java.util.ArrayList
import javax.inject.Inject
#InjectViewState
class MyProfilePresenter #Inject constructor(private val interactor: MyProfileInteractor) : MvpPresenter<MyProfileView>() {
private val userDescriptionList = ArrayList<UserDescriptionModel>()
override fun onFirstViewAttach() {
super.onFirstViewAttach()
setAllCurrentUserInfo()
setActionsCount()
}
fun setAllCurrentUserInfo() {
interactor.getAllCurrentUserInfo(UserCallback{ fsUser ->
viewState.setUserData(fsUser.name, fsUser.age, fsUser.country, fsUser.image)
userDescriptionList.addAll(UserProfileItemsList.initData(fsUser))
viewState.setList(userDescriptionList)
EventBus.getDefault().post(UserEvent(fsUser))
})
}
private fun setActionsCount() {
interactor.getActionsCountInfo(
ActionsCountInfoCallback{ visits, likes -> viewState.setActionsCount(visits, likes) })
}
#Subscribe
private fun updateActionsCount(event: VisitsEvent){
viewState.setActionsCount(event.getmVisits(), event.getmLikes())
}
fun registerSubscribers() {
if (!EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().register(this)
}
}
fun unsubscribe(){
EventBus.getDefault().unregister(this)
}
}
MyProfileActivity and its super classes have no public methods with the #Subscribe annotation
Emphasis mine. Remove the private from your function here:
#Subscribe
private fun updateActionsCount(event: VisitsEvent)
I'm using firebase_messaging in my flutter application.
To handle background messages with firebase messaging in pub they suggested to create new Application.java file and replace java file name in AndroidManifest file.
In my application i'm using kotlin and i already implemented some native code in MainActivity.kt
So how to write this code in kotlin.
package io.flutter.plugins.firebasemessagingexample;
import io.flutter.app.FlutterApplication;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback;
import io.flutter.plugins.GeneratedPluginRegistrant;
import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService;
public class Application extends FlutterApplication implements PluginRegistrantCallback {
#Override
public void onCreate() {
super.onCreate();
FlutterFirebaseMessagingService.setPluginRegistrant(this);
}
#Override
public void registerWith(PluginRegistry registry) {
GeneratedPluginRegistrant.registerWith(registry);
}
}
it is mandatory to replace MainActivity to Application in AndroidManifest file?
Here is the working background notification kotlin code:
package com.example.yourapp
import io.flutter.app.FlutterApplication
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.GeneratedPluginRegistrant
import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService
class Application : FlutterApplication(), PluginRegistrantCallback {
override fun onCreate() {
super.onCreate()
FlutterFirebaseMessagingService.setPluginRegistrant(this);
}
override fun registerWith(registry: PluginRegistry?) {
io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin.registerWith(registry?.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));
}
}
Here is the Kotlin code for the new firebase cloud messaging version:
package id.your.app
import io.flutter.app.FlutterApplication
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.firebase.messaging.FlutterFirebaseMessagingBackgroundService
// import io.flutter.plugins.firebase.messaging.FlutterFirebaseMessagingPlugin
class Application : FlutterApplication(), PluginRegistrantCallback {
override fun onCreate() {
super.onCreate()
FlutterFirebaseMessagingBackgroundService.setPluginRegistrant(this)
}
override fun registerWith(registry: PluginRegistry?) {
// FlutterFirebaseMessagingPlugin.registerWith(registry?.registrarFor("io.flutter.plugins.firebase.messaging.FlutterFirebaseMessagingPlugin"))
}
}
I started setting up dependency injection using Dagger as follows. Please feel encouraged to correct my implementation since I might have mistakes in there! The implementation follows the android-simple example provided by the project. In the following you can see how I successfully added dependency injection for Activities and Fragments. I try to keep it easy for now so I decided to inject Timber as a logger substitution for Android's log util.
import android.app.Application;
import java.util.Arrays;
import java.util.List;
import dagger.ObjectGraph;
import com.example.debugging.LoggingModule;
public class ExampleApplication extends Application {
private ObjectGraph mObjectGraph;
protected List<Object> getModules() {
return Arrays.asList(
new AndroidModule(this),
new ExampleModule(),
new LoggingModule()
);
}
private void createObjectGraphIfNeeded() {
if (mObjectGraph == null) {
Object[] modules = getModules().toArray();
mObjectGraph = ObjectGraph.create(modules);
}
}
public void inject(Object object) {
createObjectGraphIfNeeded();
mObjectGraph.inject(object);
}
}
By now the AndroidModule is not used anywhere it but might be helpful when a Context and LayoutInflater is needed e.g. in CursorAdapters.
import android.content.Context;
import android.view.LayoutInflater;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
/**
* A module for Android-specific dependencies which require a {#link Context}
* or {#link android.app.Application} to create.
*/
#Module(library = true)
public class AndroidModule {
private final ExampleApplication mApplication;
public AndroidModule(ExampleApplication application) {
mApplication = application;
}
/**
* Allow the application context to be injected but require that it be
* annotated with {#link ForApplication #Annotation} to explicitly
* differentiate it from an activity context.
*/
#Provides #Singleton #ForApplication Context provideApplicationContext() {
return mApplication;
}
#Provides #Singleton LayoutInflater provideLayoutInflater() {
return (LayoutInflater) mApplication
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
}
I am not sure what application-specific providers would go here. I stay with logging for now.
import dagger.Module;
#Module(
complete = false
)
public class ExampleModule {
public ExampleModule() {
// TODO put your application-specific providers here!
}
}
I prepared LoggingModule which provides access to Timber.
package com.example.debugging;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import com.example.BuildConfig;
import com.example.activities.BaseFragmentActivity;
import com.example.activities.DetailsActivity;
import com.example.fragments.BaseListFragment;
import com.example.fragments.ProfilesListFragment;
import timber.log.Timber;
#Module(injects = {
// Activities
BaseFragmentActivity.class,
DetailsActivity.class,
// Fragments
BaseListFragment.class,
ProfilesListFragment.class
})
public class LoggingModule {
#Provides #Singleton Timber provideTimber() {
return BuildConfig.DEBUG ? Timber.DEBUG : Timber.PROD;
}
}
The base class for Activities injects itself into the object graph ...
package com.example.activities;
import android.os.Bundle;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import javax.inject.Inject;
import com.example.ExampleApplication;
import timber.log.Timber;
public abstract class BaseFragmentActivity extends SherlockFragmentActivity {
#Inject Timber mTimber;
#Override
protected void onCreate(Bundle savedInstanceState) {
// ...
super.onCreate(savedInstanceState);
((ExampleApplication) getApplication()).inject(this);
}
}
... and any sub class benefits from Timber being already present.
package com.example.activities;
import android.os.Bundle;
import com.example.R;
public class DetailsActivity extends BaseFragmentActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_details);
mTimber.i("onCreate");
// ...
}
}
Same for Fragments: the base class does the dirty job ...
package com.example.fragments;
import android.os.Bundle;
import com.actionbarsherlock.app.SherlockListFragment;
import javax.inject.Inject;
import com.example.ExampleApplication;
import timber.log.Timber;
public abstract class BaseListFragment extends SherlockListFragment {
#Inject Timber mTimber;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
((ExampleApplication) getActivity().getApplication()).inject(this);
}
}
... and the sub class benefits from its super class.
package com.example.fragments;
import android.os.Bundle;
public class ProfilesListFragment extends BaseListFragment {
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// TODO This might be a good example to inject resources
// in the base class. But how?
setEmptyText(getResources()
.getString(R.string.profiles_list_no_content));
mTimber.i("onActivityCreated");
// ...
}
}
So far so good. But how can inject Timber into BaseCursorAdapter, BaseContentProvider, BaseSQLiteOpenHelper, BaseService, BaseAsyncTask and static helper methods?
The deprecated android example by Christopher Perry points out how to inject an Adapter into a ListFragment but not how to inject Context, Resources, LayoutInflater, Cursor into the (Cursor)Adapter or just Timber.
References:
Dagger
android-simple example
Jesse Wilson - Dagger: A Fast Dependency Injector for Android and Java
Eric Burke - Android App Anatomy
Check out Andy Dennie's examples for injecting in different scenarios:
https://github.com/adennie/fb-android-dagger
Some points where I inject:
Activity, Service, and Fragment subclasses: in onCreate
BroadcastReceiver subclasses (includes, e.g. AppWidgetProvider): in onReceive
tl;dr There's a lot going on in this question, but it might be helped with a Gist of mine. If you can get a Context somewhere you can inject it based off of the ObjectGraph maintained by your Application class.
Edit by JJD:
The ObjectGraph in the Gist can be integrated as follows:
public class ExampleApplication extends Application
implements ObjectGraph.ObjectGraphApplication {
/* Application Lifecycle */
#Override
public void onCreate() {
// Creates the dependency injection object graph
_object_graph = ObjectGraph.create(...);
}
/* ObjectGraphApplication Contract */
#Override
public void inject(#Nonnull Object dependent) {
_object_graph.inject(dependent);
}
/** Application's object graph for handling dependency injection */
private ObjectGraph _object_graph;
}
...
public abstract class BaseFragmentActivity extends SherlockFragmentActivity {
#Inject Timber _timber;
#Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
ObjectGraph.inject(this);
}
}
...
public abstract class BaseListFragment extends SherlockListFragment {
#Inject Timber _timber;
#Override
public void onActivityCreated(Bundle icicle) {
super.onActivityCreated(icicle);
ObjectGraph.inject(this);
}
}
Similar works for BaseCursorAdapter, BaseContentProvider and BaseService.
We had to do this very same thing (i.e. inject a logger everywhere). We ended up making a very small static wrapper (thus available everywhere) that wraps a class that is injected via dagger statically.
package com.example.secret;
import javax.inject.Inject;
import com.example.interfaces.Logger;
public class LoggerProvider {
#Inject
static Logger logger;
public LoggerProvider() {
}
public static Logger getLogger() {
return logger;
}
}
Logger implements your logging interface. To inject it at the application level you need:
graph = ObjectGraph.create(getModules().toArray());
graph.injectStatics();
Code here: https://github.com/nuria/android-examples/tree/master/dagger-logger-example
Injection of Context, Resources and LayoutInflater (passing the application context when you new this up in Application).
#Module(complete = false)
public class AndroidServicesModule {
private final Context context;
public AndroidServicesModule(#ForApplication Context context) {
this.context = context;
}
#Provides #Singleton Resources provideResources() {
return context.getResources();
}
#Provides #Singleton LocationManager provideLocationManager() {
return (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
}
#Provides #Singleton LayoutInflater provideLayoutInflater() {
return (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Provides #Singleton Resources provideResources() {
return context.getResources();
}
#Provides #ForApplication Context provideContext() {
return context;
}
}
Of course you should probably qualify the context with an annotation specifying it's the application context (for example #ForApplication).
If you need the equivalent of Roboguice's #InjectResource(int) I can't think of anything offhand. Butterknife seems like the right lib to add this to. See here
You can add these constructions to ExampleApplication class:
private static ExampleApplication INSTANCE;
#Override
public void onCreate() {
super.onCreate();
INSTANCE = this;
mObjectGraph = ObjectGraph.create(getModules());
mObjectGraph.injectStatics();
mObjectGraph.inject(this);
}
public static ExampleApplication get() {
return INSTANCE;
}
After that you are able to inject any object (denoted by this) with one simple line:
ExampleApplication.get().inject(this)
This should be called in constructor for objects you create manually or onCreate (or analog) for those that are managed by Android System.
I want to start using Robolectric and RoboGuice in my Android-Apps. While I make satisfactory progress using Robolectric I am stuck using RoboGuice. I created a small Android-App for experimenting. It is only one Activity, injecting a Button and setting its OnClickListener.
In the related Test-Class I want to Inject this Activity, to be able to test the Button. I tried a lot of things I found all over the internet, but none of these worked, so I give it a go here.
Here is some code:
MainActivity.java:
package com.example.TrialApp;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import roboguice.activity.RoboActivity;
import roboguice.inject.InjectView;
public class MainActivity extends RoboActivity implements View.OnClickListener {
#InjectView(R.id.main_LoginButton) private Button loginButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
loginButton.setOnClickListener(this);
}
#Override
public void onClick(View view) {
SharedPreferences.Editor editor;
if (view.getId() == R.id.main_Login_Button)
Log.i("Login-Button pressed... ", "");
}
}
CustomRobolectricTestRunner.java:
package com.example.TrialApp;
import com.xtremelabs.robolectric.RobolectricTestRunner;
import org.junit.runners.model.InitializationError;
import java.io.File;
public class CustomRobolectricTestRunner extends RobolectricTestRunner {
public CustomRobolectricTestRunner(Class testClass) throws InitializationError {
// defaults to "AndroidManifest.xml", "res" in the current directory
super(testClass, new File("TrialApp"));
}
}
MainActivity_Test.java:
package com.example.TrialApp;
import com.google.inject.Inject;
import com.xtremelabs.robolectric.Robolectric;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertNotNull;
#RunWith(CustomRobolectricTestRunner.class)
public class MainActivity_Test {
#Inject MainActivity mainActivity;
#Inject ClassWithoutAName classWithoutAName;
#Before
public void setUp() {
}
#Test
public void mainActivityShouldNotBeNull() {
assertNotNull(mainActivity);
}
#Test
public void classWithoutANameShouldNotBeNull() {
assertNotNull(classWithoutAName);
}
}
classWithoutAName is just a non-Activity-class with no content. I added just for injecting a non-Activity-class.
Running the Test-Class both tests fail giving the following errors:
java.lang.AssertionError
at com.example.TrialApp.MainActivity_Test.mainActivityShouldNotBeNull(MainActivity_Test.java:33) <8 internal calls>
at com.xtremelabs.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:288) <16 internal calls>
and
java.lang.AssertionError
at com.example.TrialApp.MainActivity_Test.classWithoutANameShouldNotBeNull(MainActivity_Test.java:38) <8 internal calls>
at com.xtremelabs.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:288) <16 internal calls>
Obviously something is missing. Injecting in MainActivity works fine, and the app is running.
Why is the same pattern of injecting dependencies in a Robolectric-Testclass not working? Where is the missing link?
Thank you
After reading some more articles and threads I found a partial solution. I still haven't found a way to inject UI-elements in a testclass, but I found out, how to inject non-UI-elements in a testclass. The trick is to implement a "RobolectricTestModule" extending the AbstractModule. The bindings made in the modules declared in the roboguice.xml are not present in the test-environment, so we need to declare the bindings for the test-environment in this extra-module. In the customized Testrunner we replace the DefaultRoboModule with the new RobolectricTestModule.
ClassWithoutAName.java:
package com.example.TrialApp;
public class ClassWithoutAName {
private String string;
public ClassWithoutAName(String string) {
this.string = string;
}
public String getString() {
return string;
}
}
ClassWithoutANameProvider.java
package com.example.TrialApp.GuiceModules;
import com.example.TrialApp.ClassWithoutAName;
import com.google.inject.Provider;
public class ClassWithoutANameProvider implements Provider<ClassWithoutAName> {
#Override
public ClassWithoutAName get() {
return new ClassWithoutAName("testString");
}
}
ClassWithOutANameModule.java
package com.example.TrialApp.GuiceModules;
import com.example.TrialApp.ClassWithoutAName;
import com.google.inject.AbstractModule;
public class ClassWithOutANameModule extends AbstractModule {
#Override
protected void configure() {
bind(ClassWithoutAName.class).toProvider(ClassWithoutANameProvider.class);
}
}
RobolectricTestModule.java
package com.example.TrialApp.GuiceModules;
import com.example.TrialApp.WeirdThings;
import com.google.inject.AbstractModule;
public class RobolectricTestModule extends AbstractModule {
#Override
protected void configure() {
bind(ClassWithoutAName.class).toProvider(ClassWithoutANameProvider.class);
}
}
CustomRobolectricTestRunner.java
package com.example.TrialApp;
import android.app.Application;
import com.example.TrialApp.GuiceModules.RobolectricTestModule;
import com.xtremelabs.robolectric.Robolectric;
import com.xtremelabs.robolectric.RobolectricTestRunner;
import org.junit.runners.model.InitializationError;
import roboguice.RoboGuice;
import java.io.File;
public class CustomRobolectricTestRunner extends RobolectricTestRunner {
public CustomRobolectricTestRunner(Class testClass) throws InitializationError {
// defaults to "AndroidManifest.xml", "res" in the current directory
super(testClass, new File("TrialApp"));
}
#Override
public void prepareTest(Object test) {
Application application = (Application) Robolectric.application;
RoboGuice.setBaseApplicationInjector(application, RoboGuice.DEFAULT_STAGE,
RoboGuice.newDefaultRoboModule(application), new RobolectricTestModule());
RoboGuice.getInjector(application).injectMembers(test);
}
}
Thats it for the non-UI-Elements.