How to get the activity reference before its oncreate gets called during testing
Asked Answered
M

3

10

How to get the reference of Activity before its onCreate will be called. while its under test. I use ActivityTestRule as JUnit Rule. The reason for this requirement is i want to inject Mocks into activity from tests.

public class MyActivity extends Activity{

    MyComponent myComponent;

    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        if(myComponent==null){
            myComponent ... //initialise dagger component
        }
        myComponent.inject(this);
        ...
    }

    public void setComponent(MyComponent comp){
        this.myComponent = comp;
    }
}

public class MyTest{

    @Rule
    public ActivityTestRule<MyActivity> intentsTestRule = new ActivityTestRule<>(MyActivity.class);


    MyComponent myFakeComponent;

    @Before                                      
    public void setUp() {                        
        MyActivity activity = intentsTestRule.getActivity();  
        activity.setComponent(myFakeComponent);
    }                                            

    @Test
    public void testMethod1(){...}
} 
Martinemartineau answered 13/7, 2015 at 16:39 Comment(1)
Do you use Dagger? If yes where do you initialise Dagger's modules?Osage
N
19

As per documentation, what you're doing here is wrong.

@Rule
public ActivityTestRule<MyActivity> intentsTestRule = new ActivityTestRule<>(MyActivity.class);

MyComponent myFakeComponent;

@Before                                      
public void setUp() {                        
    MyActivity activity = intentsTestRule.getActivity();  
    activity.setComponent(myFakeComponent);
}              

Because,

This rule provides functional testing of a single activity. The activity under test will be launched before each test annotated with Test and before methods annotated with @Before. It will be terminated after the test is completed and methods annotated with After are finished. During the duration of the test you will be able to manipulate your Activity directly.

However!

protected void beforeActivityLaunched ()

Override this method to execute any code that should run before your Activity is created and launched. This method is called before each test method, including any method annotated with @Before.

Therefore, if you move the initialization of the MainActivityComponent outside the Activity to a place that is mockable, then you'll be able to tinker it together before the main activity is created.

EDIT:

Another possible solution is to lazily initiate the Activity as per link.

@Rule
public ActivityTestRule<NoteDetailActivity> mNoteDetailActivityTestRule =
        new ActivityTestRule<>(NoteDetailActivity.class, true /* Initial touch mode  */,
                false /* Lazily launch activity */);

@Before
public void intentWithStubbedNoteId() {
   // Add a note stub to the fake service api layer.
   FakeNotesServiceApiImpl.addNotes(NOTE);

   // Lazily start the Activity from the ActivityTestRule this time to inject the start Intent
   Intent startIntent = new Intent();
   startIntent.putExtra(NoteDetailActivity.EXTRA_NOTE_ID, NOTE.getId());
   mNoteDetailActivityTestRule.launchActivity(startIntent);

   registerIdlingResource();
}
Noose answered 14/1, 2016 at 15:41 Comment(4)
new ActivityScenarioRule<MainActivity>(....class, true, false) is undefinedChukker
It worked when I posted the answer, so it's either changed slightly in a newer version, or you're trying to use new in Kotlin which is no longer a keyword.Noose
I'm a Kotlin denier; I should have pointed out I'm using Java. But I have since answered my outer question - how to deprecate ActivityTestRule<> - here: #63817172Chukker
Do you know how to achive that with ActivityScenario ?Pastiness
G
1

Here is my sample code for that:

public class TestClass {

    @Rule
    public ActivityTestRule<T> activityRule = new ActivityTestRule<T>(type) {
            @Override
            protected void beforeActivityLaunched() {
                //TODO inject mocks, setup stubs etc..
            }
        };
    }

    @Before
    public void before() {
        activityRule.getActivity();
    }

    @Test
    public void myTest() {
        //...
    }


}
Ghislainegholston answered 14/10, 2016 at 8:16 Comment(0)
G
0

Is this code complete? I can't see you creating the dagger graph. Anyway, what I do in my code, is to have a Static class called Injector that creates the graph for me, and also can inject elements into objects. So, in my Application Class I call it to create the graph, and all other activities just use the existent graph.

Then, in a test, you could create a fake test application class that initialize the graph in a different way, or simply recreate the graph calling the Injector methods, before the activity is created. I'm not familiar with ActivityTestRule, so I can't help much with the life cycle of this test.

But just make sure you create a new graph before the activity is created, and let the activity just use the existent graph. How the activity access the graph? Well, I don't really love it, but we are used to access the application class (with explicit cast) and ask it to inject the dependencies for us. This is the way Dagger examples do it also.

Gosport answered 4/8, 2015 at 5:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.