Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error invoking setter with parameter class on ObjectWrapper getSetterMethod #77

Open
bparikh20 opened this issue Jun 17, 2018 · 7 comments

Comments

@bparikh20
Copy link

Hi
I followed the example on to create a custom DaggerMock Rule and I am getting error
I/TestRunner: java.lang.RuntimeException: Error invoking setter with parameter class com.visa.cardcontrols.di.AppModule on object com.visa.cardcontrols.di.DaggerAppComponent$Builder@ba12a92
at it.cosenonjaviste.daggermock.ObjectWrapper.invokeBuilderSetter(ObjectWrapper.java:134)
at it.cosenonjaviste.daggermock.DaggerMockRule.initComponent(DaggerMockRule.java:206)
at it.cosenonjaviste.daggermock.DaggerMockRule.access$300(DaggerMockRule.java:35)
at it.cosenonjaviste.daggermock.DaggerMockRule$1.evaluate(DaggerMockRule.java:105)
at android.support.test.internal.statement.UiThreadStatement.evaluate(UiThreadStatement.java:55)
at android.support.test.rule.ActivityTestRule$ActivityStatement.evaluate(ActivityTestRule.java:270)
at android.support.test.internal.statement.UiThreadStatement.evaluate(UiThreadStatement.java:55)
at android.support.test.rule.ActivityTestRule$ActivityStatement.evaluate(ActivityTestRule.java:270)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:59)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:262)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1932)
Caused by: java.lang.NoSuchMethodException: AppModule [class com.visa.cardcontrols.di.AppModule]
at java.lang.Class.getMethod(Class.java:1981)
at java.lang.Class.getMethod(Class.java:1637)
at it.cosenonjaviste.daggermock.ObjectWrapper.getSetterMethod(ObjectWrapper.java:144)
at it.cosenonjaviste.daggermock.ObjectWrapper.invokeBuilderSetter(ObjectWrapper.java:131)
at it.cosenonjaviste.daggermock.DaggerMockRule.initComponent(DaggerMockRule.java:206) 
at it.cosenonjaviste.daggermock.DaggerMockRule.access$300(DaggerMockRule.java:35) 
at it.cosenonjaviste.daggermock.DaggerMockRule$1.evaluate(DaggerMockRule.java:105) 
at android.support.test.internal.statement.UiThreadStatement.evaluate(UiThreadStatement.java:55) 
at android.support.test.rule.ActivityTestRule$ActivityStatement.evaluate(ActivityTestRule.java:270) 
at android.support.test.internal.statement.UiThreadStatement.evaluate(UiThreadStatement.java:55) 
at android.support.test.rule.ActivityTestRule$ActivityStatement.evaluate(ActivityTestRule.java:270) 
at org.junit.rules.RunRules.evaluate(RunRules.java:20) 
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) 
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) 
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) 
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) 
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) 
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) 
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) 
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) 
at org.junit.runners.ParentRunner.run(ParentRunner.java:363) 
at org.junit.runners.Suite.runChild(Suite.java:128) 
at org.junit.runners.Suite.runChild(Suite.java:27) 
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) 
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) 
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) 
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) 
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) 
at org.junit.runners.ParentRunner.run(ParentRunner.java:363) 
at org.junit.runner.JUnitCore.run(JUnitCore.java:137) 
at org.junit.runner.JUnitCore.run(JUnitCore.java:115) 
at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:59) 
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:262) 
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1932) 
----- end exception -----

I debugged it and seems the error occurs on objectwrapper.java class get SetterMethod

String moduleName = moduleClass.getSimpleName();
String setterName = ReflectUtils.toCamelCase(moduleName);
return builder.getClass().getMethod(setterName, moduleClass);

The code for the custom rule is below

public class CardControlAndroidTestDaggerRule extends DaggerMockRule {
public CardControlAndroidTestDaggerRule() {
super(AppComponent.class, new AppModule());
providesMock(TravelRepository.class);
set(component -> getApp().setComponent(component));
}

public static CardControlApp getApp() {
    return (CardControlApp) InstrumentationRegistry.getInstrumentation()
            .getTargetContext().getApplicationContext();
}

and my appcomponent class is
package com.visa.cardcontrols.di;

import android.app.Application;
import android.content.Context;

import com.visa.cardcontrols.CardControlApp;

import javax.inject.Singleton;

import dagger.BindsInstance;
import dagger.Component;
import dagger.android.AndroidInjectionModule;
import dagger.android.support.AndroidSupportInjectionModule;

@singleton
@component(modules = {
AndroidInjectionModule.class,
AppModule.class,
ActivityBuilderModule.class})
public interface AppComponent {

@Component.Builder
interface Builder {
    @BindsInstance
    Builder context(Context context);
    @BindsInstance
    Builder application(Application application);
    AppComponent build();
}

void inject(CardControlApp app);

}

@fabioCollini
Copy link
Owner

Hi, can you try to add a method to component builder with the AppModule as parameter? Something like the two methods in this example: https://github.com/fabioCollini/DaggerMock/blob/master/RealWorldAppInjector/src/main/java/it/cosenonjaviste/daggermock/realworldapp/AppComponent.java

@bparikh20
Copy link
Author

Hi I was able to get around the problem by using the customizeBuilder example... However I am finding that the mocks are not being used and the actual repository object is being called.. Below is my code let me know what I am doing wrong

Custom Dagger Mock Rule
public class CardControlAndroidTestDaggerRule extends DaggerMockRule {
public CardControlAndroidTestDaggerRule() {
super(AppComponent.class, new AppModule());
customizeBuilder((BuilderCustomizer<AppComponent.Builder>) builder -> {
builder.application(getApp());
builder.context(getApp());
return builder;
});
set(component -> getApp().setComponent(component));
}

private static CardControlApp getApp() {
    return (CardControlApp) InstrumentationRegistry.getInstrumentation()
            .getTargetContext().getApplicationContext();
}

}

My APP Module
@module(includes = TravelControlModule.class)
public class AppModule {

@Provides
@Singleton
protected RequestInterceptor providesRequestInterceptor(Context context) {
    return new RequestInterceptor(context);
}

@Provides
@Singleton
// Application reference must come from AppModule.class
protected SharedPreferences providesSharedPreferences(Application application) {
    return PreferenceManager.getDefaultSharedPreferences(application);
}

@Provides
@Singleton
protected OkHttpClient provideOkHttpClient(HttpLoggingInterceptor httpLoggingInterceptor,
                                           RequestInterceptor requestInterceptor) {
    OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder();
    okHttpClient.connectTimeout(Constants.TIMEOUT_IN_SEC, TimeUnit.SECONDS);
    okHttpClient.readTimeout(Constants.TIMEOUT_IN_SEC, TimeUnit.SECONDS);
    okHttpClient.addInterceptor(httpLoggingInterceptor);
    okHttpClient.addInterceptor(requestInterceptor);
    return okHttpClient.build();
}

..........
......
........

@Provides
@Singleton
protected TravelRepository providesTravelRepository(Context context, ItineraryDao itineraryDao, TNSService itineraryService) {
    return new TravelRepository(context, itineraryDao, itineraryService);
}

}

ActivityBuilderModule

@module
public abstract class ActivityBuilderModule {

@ContributesAndroidInjector(modules = {TravelControlModule.class})
abstract TravelControlMainPage travelControlsMainPage();

@ContributesAndroidInjector(modules = {TravelControlModule.class})
abstract TravelControlsTrip travelControlsTrip();

@ContributesAndroidInjector(modules = {TravelControlModule.class})
abstract TravelControlTripConfirmed travelControlTripConfirmed();

}

and my APPComponent

@singleton
@component(modules = {
AndroidInjectionModule.class,
AppModule.class,
ActivityBuilderModule.class})
public interface AppComponent {

@Component.Builder
interface Builder {
    @BindsInstance
    Builder context(Context context);

    @BindsInstance
    Builder application(Application application);

    /* This method is necessary for DaggerMock when running instrumentations tests */
    Builder appModule(AppModule appModule);

    AppComponent build();
}

void inject(CardControlApp app);

}

@fabioCollini
Copy link
Owner

Now it should work, ho do you define the repository in your test? Do you use it in your activity or in a Presenter/ViewModel?

@bparikh20
Copy link
Author

Thanks for your help... I am using Dagger 2 and MVVM. The below is code for one of my tests.
I am defining the mock repository but its not being mocked...
{
@RunWith(AndroidJUnit4.class)
public class TravelControlViewModelAndroidTest {

@Rule
public final ActivityTestRule<TravelControlsTrip> activityTestRule =
        new ActivityTestRule<>(TravelControlsTrip.class, false, false);

@Rule
public final ActivityTestRule<TravelControlMainPage> mainPageActivityTestRule =
        new ActivityTestRule<>(TravelControlMainPage.class, false, false);

@Rule public CardControlAndroidTestDaggerRule cardControlAndroidTestDaggerRule = new CardControlAndroidTestDaggerRule();

@Mock
TravelRepository travelRepository;

@Mock
LiveData<Resource<List<ItineraryEntity>>> itineraryEntityList;

@Test
public void openTripPage() {
    TravelControlsTrip activity = activityTestRule.launchActivity(null);

    onView(withId(activity.binding.textviewPaymentAlerts.getId()))
            .check(matches(isDisplayed()));
}

@Test
public void onMlcClicked() {
    when(travelRepository.getAllItinerary()).thenReturn(itineraryEntityList);

    TravelControlMainPage activity = mainPageActivityTestRule.launchActivity(null);
    onView(withId(activity.binding.switchLoc.getId())).perform(click());

    onView(withId(android.R.id.button1))
            .inRoot(isDialog())
            .check(matches(withText(Constants.ALLOW)))
            .check(matches(isDisplayed()));

    onView(withId(android.R.id.button2))
            .inRoot(isDialog())
            .check(matches(withText(Constants.IGNORE)))
            .check(matches(isDisplayed()));

    onView(withId(android.R.id.button2)).perform(click());

}

}

@fabioCollini
Copy link
Owner

Where do you define the TravelRepository object? If you define it in TravelControlModule module you should pass it to the super constructor of CardControlAndroidTestDaggerRule.

@bparikh20
Copy link
Author

No I have defined travelrepository in the App Module class..
{
@provides
@singleton
protected TravelRepository providesTravelRepository(Context context, ItineraryDao itineraryDao, TNSService itineraryService) {
return new TravelRepository(context, itineraryDao, itineraryService);
}
}

which is then defined in the dagger test rule
{
public class CardControlAndroidTestDaggerRule extends DaggerMockRule {
public CardControlAndroidTestDaggerRule() {
super(AppComponent.class, new AppModule());
customizeBuilder((BuilderCustomizer<AppComponent.Builder>) builder -> {
builder.application(getApp());
builder.context(getApp());
return builder;
});
set(component -> getApp().setComponent(component));
}

private static CardControlApp getApp() {
return (CardControlApp) InstrumentationRegistry.getInstrumentation()
.getTargetContext().getApplicationContext();
}
}

@fabioCollini
Copy link
Owner

Can you try to add some breakpoints to try the error? Are you sure to use the DaggerMock component in your application? Can you check stacktrace of the repository constructor invocation to check if it contains some DaggerMock methods?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants