I can't inject dependencies through the constructor and I'm sure that I'm doing something wrong.
I have the following:
public class Water {
#Inject
public Water() {}
#Override
public String toString() { return "Water + ";}
}
public class Heater {
#Inject
public Heater() {}
#Override
public String toString() {return " heater";}
}
public class Aeropress {
Water mWater;
Heater mHeater;
#Inject
public Aeropress(Water water, Heater heater) {
mWater = water;
mHeater = heater;
}
#Override
public String toString() {
return mWater.toString() + mHeater.toString();
}
}
public class LoginActivity extends AppCompatActivity{
#Inject Aeropress aeropress;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e("* 69 * LoginActivity *", "onCreate " + aeropress);
}
The code in activity prints null so dagger doesn't inject anything. Any idea how to solve this without using #provide ? What am I missing ?
In order to do this you have to do 2 things:
1. Declare a component with an inject method
#Component
public interface AeropressComponent {
void inject(LoginActivity aeropress);
}
2. Build the dagger component in your activity and inject it
public class LoginActivity extends AppCompatActivity{
#Inject Aeropress aeropress;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerAeropressComponent.builder().build().inject(this);
/*DaggerAeropressComponent.create().inject(this);*/ //or this
Log.e("* 69 * LoginActivity *", "onCreate " + aeropress);
}
After these modifications, it works like a charm without even creating a #Module class. The logic how I understand this and the reason why you need a component is that in order for dagger to inject a new instance into aeropress, it needs the container(the activity) where a reference of aeropress can be found. Also I just remembered now that the reason why #Inject fields can't be private is that dagger makes direct assignment between the reference and the created instance, in my example it does the following
LoginActivity.aeropress = Factory.createAeropress();
So without creating the component with the inject method, it can't know where to put the instance created with Factory.createAeropress();
If anyone can give me a better solution I'll mark the answer
Related
I create a unit test for my Presenter. My Presenter implements Listener callback if successfully load data from API (use Interactor):
PresenterTest.java
public class MainContactPresenterTest {
#Mock LoadContactInteractor loadContactInteractor;
#Mock ApiService apiService;
#Mock LoadContactView loadContactView;
#Mock ContactRepository contactRepository;
#Mock LoadContactInteractor.OnLoadDataFinishedListener listener;
#InjectMocks MainContactPresenterImpl presenter;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void getContactLists() {
// given
// when
presenter.fetchRemoteContacts();
// then
Mockito.verify(loadContactInteractor).onLoadData(listener);
}
}
Here is my Presenter:
public class MainContactPresenterImpl implements MainContactPresenter,
LoadContactInteractor.OnLoadDataFinishedListener {
private LoadContactView loadContactView;
private LoadContactInteractor loadContactInteractor;
private ContactRepository contactRepository;
#Inject
public MainContactPresenterImpl(LoadContactInteractor loadContactInteractor,
#NonNull LoadContactView loadContactView,
ContactRepository contactRepository) {
this.loadContactView = loadContactView;
this.loadContactInteractor = loadContactInteractor;
this.contactRepository = contactRepository;
}
#Override
public void onSuccessLoad(List<Contact> contacts) {
loadContactView.saveDataToLocalStorage(contacts);
}
#Override
public void onErrorLoad() {
loadContactView.dismissProgress();
loadContactView.showErrorMessage();
}
#Override
public void preCheckCacheData() {
if (contactRepository.getContactCount() == 0) {
// Load contacts from Server
fetchRemoteContacts();
} else {
fetchLocalContacts();
}
}
#Override
public void fetchRemoteContacts() {
loadContactView.showProgress();
loadContactInteractor.onLoadData(this);
}
}
But when I ran test, I got the mocking parameter in verify not match.
I got my presenter that have to be an argument. Not the listener.
Argument(s) are different! Wanted:
loadContactInteractor.onLoadData(
listener
);
Actual invocation has different arguments:
loadContactInteractor.onLoadData(
fanjavaid.gojek.com.contacts.presenter.MainContactPresenterImpl#1757cd72
);
How to handle that? Thank you
You are creating a mock...
#Mock LoadContactInteractor.OnLoadDataFinishedListener listener;
...and then you don't use it ever again and act suprised when verify tells you, that it wasn't actually used. Why? Of course it wasn't used, since you never use it anywhere, so how should your classes know to use that mock object?
Your MainContactPresenterImpl does not use an OnLoadDataFinishedListener as an external dependency (then your could perhaps inject it via #InjectMocks), it is itself such a listener and thus mocking another listener makes no sense here.
In other words, MainContactPresenterImpl has no OnLoadDataFinishedListener field, so Mockito is of course not capable of injecting something in this non-existing field. For something like this to work, you would need to add such a field and then use the content of that field when calling your onLoadData method.
The only invocation of your method is here...
loadContactInteractor.onLoadData(this);
And what is this in that context? It's the MainContactPresenterImpl object that contains the method, in other words, your presenter.
So, what will work is...
Mockito.verify(loadContactInteractor).onLoadData(presenter);
I'm trying to inject DataSource class to Presenter using Dagger 2, but dataSource is null.
The code is below :
public class MainPresenter implements MainMVP.Presenter {
public static final String TAG = "MAIN-PRESENTER";
#NonNull
private MainMVP.View mainView;
#Inject
DataSource dataSource;
public MainPresenter(#NonNull MainMVP.View mainView) {
this.mainView = mainView;
Log.i(TAG, "MainPresenter init");
DaggerDataComponent.builder()
.dataModule(new DataModule())
.build();
}
#Override
public void onButtonClick() {
if (dataSource != null) {
mainView.showData(dataSource.getReleaseString());
}
}
}
If I remove the condition that checks for null in dataSource I'm getting a NullPointerException. Anyone can help with that? Isn't the constructor the right place to build the DataComponent ?
You're building your component but you don't seem to be actually using it.
DataCompontent component = DaggerDataComponent.builder()
.dataModule(new DataModule())
.build();
component.inject(this);
and add
void inject(MainPresenter presenter);
to your DataComponent interface.
As for your question about if that is the right place to build your component: we can't really answer that. That strongly depends on your code architecture.
Here's a nice example of MVP + dagger2 architecture. Maybe try following that.
Im trying to do a very simple dependency injection in a Android app. I am using dagger 2 as a DI tool.
The issue is no injection is occuring:
here is my code:
//behold Motor.java in all its awe.
public class Motor {
private int rpm;
public Motor(){
this.rpm = 10; //default will be 10
}
public int getRpm(){
return rpm;
}
public void accelerate(int value){
rpm = rpm + value;
}
public void brake(){
rpm = 0;
}
}
and here is the Vehicle.java which utilities the motor class:
import javax.inject.Inject;
public class Vehicle {
private Motor motor;
#Inject
public Vehicle(Motor motor){
this.motor = motor;
}
public void increaseSpeed(int value){
motor.accelerate(value);
}
public void stop(){
motor.brake();
}
public int getSpeed(){
return motor.getRpm();
}
}
I then created a VehicleModule.java class to define my provider:
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
#Module
public class VehicleModule {
#Provides
#Singleton
Motor provideMotor(){
return new Motor();
}
#Provides #Singleton
Vehicle provideVehicle(){
return new Vehicle(new Motor());
}
}
I then i have a interface component annotated, defined like this:
import javax.inject.Singleton;
import Modules.VehicleModule;
import dagger.Component;
#Singleton
#Component(modules = {VehicleModule.class})
public interface VehicleComponent {
Vehicle provideVehicle();
}
and here is my Android mainactivity class that should be injected but its not, can anyone help:
import javax.inject.Inject;
public class MainActivity extends ActionBarActivity {
#Inject
Vehicle vehicle;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toast.makeText(this, String.valueOf(vehicle.getSpeed()), Toast.LENGTH_SHORT).show();
}
}
im getting a null pointer exception on vehicle:
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int com.example.uen229.myapplication.Vehicle.getSpeed()' on a null object reference
by the way my gradle dependencies look like this:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.2.0'
compile 'com.google.dagger:dagger:2.0'
}
here is my Android mainactivity class that should be injected but its not
You're expecting magical things happen when you annotate something with #Inject. While magical things will happen, it's not that magical. You will need to do that yourself, by instantiating the component implementations that Dagger generated.
You can do this in a couple of ways, I will describe two.
First, in your MainActivity's onCreate:
private Vehicle vehicle; // Note, no #Inject annotation
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
VehicleComponent vehicleComponent = DaggerVehicleComponent.create();
this.vehicle = vehicleComponent.provideVehicle();
Toast.makeText(this, String.valueOf(vehicle.getSpeed()), Toast.LENGTH_SHORT).show();
}
In this case, you create an instance of VehicleComponent, implemented by Dagger, and fetch the Vehicle instance from it. The vehicle field is not annotated by #Inject. This has the advantage that the field can be private, which is a good thing to want.
Secondly, if you do want Dagger to inject your fields, you need to add an inject method to your VehicleComponent:
#Singleton
#Component(modules = {VehicleModule.class})
public interface VehicleComponent {
Vehicle provideVehicle();
void inject(MainActivity mainActivity);
}
In your MainActivity class, you call inject(this), which will fill the vehicle field:
#Inject
Vehicle vehicle; // Note, package-local
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
VehicleComponent vehicleComponent = DaggerVehicleComponent.create();
vehicleComponent.inject(this);
Toast.makeText(this, String.valueOf(vehicle.getSpeed()), Toast.LENGTH_SHORT).show();
}
This brings a bit of extra configuration, but is sometimes necessary.
I like the first method however.
As a final comment, let's have a look at your VehicleModule, and really use the power of Dagger.
Instead of using the module to create the instances yourself, you can make Dagger to that for you. You've already annotated the Vehicle constructor with #Inject, so Dagger will know to use this constructor. However, it needs an instance of Motor, which it doesn't know of. If you add an #Inject annotation to the constructor of Motor as well, and annotate the Motor class with #Singleton, you can get rid of the VehicleModule altogether!
For example:
#Singleton
public class Motor {
private int rpm;
#Inject // Just an annotation to let Dagger know how to retrieve an instance of Motor.
public Motor(){
this.rpm = 10; //default will be 10
}
public int getRpm(){
return rpm;
}
public void accelerate(int value){
rpm = rpm + value;
}
public void brake(){
rpm = 0;
}
}
Your Vehicle class:
#Singleton
public class Vehicle {
private Motor motor;
#Inject
public Vehicle(Motor motor){
this.motor = motor;
}
public void increaseSpeed(int value){
motor.accelerate(value);
}
public void stop(){
motor.brake();
}
public int getSpeed(){
return motor.getRpm();
}
}
You can now safely delete the VehicleModule class, and remove the reference to it in your VehicleComponent.
you are missing the following steps
you have create the module like this in application class
public class D2EApplication extends Applicaiton {
public static D2EComponent component(Context context) {
return ((D2EBaseApplication) context.getApplicationContext()).component;
}
public final static class DaggerComponentInitializer {
public static D2EComponent init(D2EBaseApplication app) {
return DaggerD2EComponent.builder()
.systemServicesModule(new VehicleModule(app))
.build();
}
}
}
and inject it to the activity
D2EApplication.component(this).inject(this);
My base POJO class:
public class BaseDao {
public BaseDao() {
}
// ...
}
My extends POJO class:
public class KelvinDao extends BaseDao {
public KelvinDao () {
super();
}
// ...
}
I want to use KelvinDao in a service like that:
public class HanKelvinHandler extends HttpRequestHandler {
#Inject
private KelvinDao mKelvinDao;
public void treatGet() {
mKelvinDao.blabla(); !!! mKelvinDao is always NULL
}
It's really simple but it doesn't work :(
Thank you guys for your help!
How are you creating HanKelvinHandler? If you're doing it within a subclass of a RoboGuice class, such as RoboActivity, then it should just work. Example:
public class MyActivity extends RoboActivity
{
#Inject
private HanKelvinHandler m_handler;
[...]
}
Otherwise (i.e., you're creating it within another POJO), you're in regular Guice land, and I believe you will need to use the injector to get an instance of it. Example:
public class MyClass
{
public void doSomething()
{
Injector injector = Guice.createInjector( new YourGuiceBindings() );
HanKelvinHandler handler = injector.getInstance( HanKelvinHandler.class );
handler.treatGet(); // mKelvinDao should be good now
}
}
If you haven't seen the use of the injector before, or you don't know what to put for YourGuiceBindings(), then you may need to read the following:
https://github.com/roboguice/roboguice/wiki/Simple-Custom-Binding
https://code.google.com/p/google-guice/wiki/GettingStarted
It seems like there should be a way to do this without using the injector, but I don't know.
I’m trying to test an Activity with Mockito & Dagger. I have been able to inject dependencies to Activity in my application but when testing the Activity, I have not been able to inject mock to the Activity. Should I inject Activity to test or let getActivity() create it?
public class MainActivityTest extends
ActivityInstrumentationTestCase2<MainActivity> {
#Inject Engine engineMock;
private MainActivity mActivity;
private Button mLogoutBtn;
public MainActivityTest() {
super(MainActivity.class);
}
#Override
protected void setUp() throws Exception {
super.setUp();
// Inject engineMock to test
ObjectGraph.create(new TestModule()).inject(this);
}
#Override
protected void tearDown() {
if (mActivity != null)
mActivity.finish();
}
#Module(
includes = MainModule.class,
entryPoints = MainActivityTest.class,
overrides = true
)
static class TestModule {
#Provides
#Singleton
Engine provideEngine() {
return mock(Engine.class);
}
}
#UiThreadTest
public void testLogoutButton() {
when(engineMock.isLoggedIn()).thenReturn(true);
mActivity = getActivity();
mLogoutBtn = (Button) mActivity.findViewById(R.id.logoutButton);
// how to inject engineMock to Activity under test?
ObjectGraph.create(new TestModule()).inject(this.mActivity);
assertTrue(mLogoutBtn.isEnabled() == true);
}
}
I use Mockito and Dagger for functional testing.
The key concept is that your test class inherits from ActivityUnitTestCase, instead of ActivityInstrumentationTestCase2; the latter super-class call onStart() life-cycle method of Activity blocking you for inject your test doubles dependencies, but with first super-class you can handle the life-cycle more fine-grained.
You can see my working examples using dagger-1.0.0 and mockito for test Activities and Fragments in:
https://github.com/IIIRepublica/android-civicrm-test
The project under test is in:
https://github.com/IIIRepublica/android-civicrm
Hope this helps you
I did some more experimenting and found out that Dagger is not able to create activity correctly when it is injected to test. In the new version of test, testDoSomethingCalledOnEngine passes but onCreate is not called on the MainActivity. The second test, testDoSomethingUI fails and there are actually two instances of MainActivity, onCreate gets called to the other instance (created by ActivityInstrumentationTestCase2 I quess) but not to the other. Maybe the developers at Square only thought about testing Activites with Robolectric instead of Android instrumentation test?
public class MainActivityTest extends
ActivityInstrumentationTestCase2<MainActivity> {
#Inject Engine engineMock;
#Inject MainActivity mActivity;
public MainActivityTest() {
super(MainActivity.class);
}
#Override
protected void setUp() throws Exception {
super.setUp();
// Inject engineMock to test & Activity under test
ObjectGraph.create(new TestModule()).inject(this);
}
#Module(
includes = MainModule.class,
entryPoints = MainActivityTest.class,
overrides = true
)
static class TestModule {
#Provides
#Singleton
Engine provideEngine() {
return mock(Engine.class);
}
}
public void testDoSomethingCalledOnEngine() {
when(engineMock.isLoggedIn()).thenReturn(true);
mActivity.onSomethingHappened();
verify(engineMock).doSomething();
}
#UiThreadTest
public void testDoSomethingUI() {
when(engineMock.isLoggedIn()).thenReturn(true);
mActivity.onSomethingHappened();
Button btn = (Button) mActivity.findViewById(R.id.logoutButton);
String btnText = btn.getText().toString();
assertTrue(btnText.equals("Log out"));
}
}
I have put everything together and made demo app that shows how to test with dagger: https://github.com/vovkab/dagger-unit-test
Here is my pervious answer with more details:
https://stackoverflow.com/a/24393265/369348