How to tell Junit/Mockito to wait for AndroidAnnotations to inject the dependenices
Asked Answered
R

1

0

I am using AndroidAnnotations in my project and I want to test a presenter.

The test suite runs and @Test methods are apparently called before the injection has finished, because I get NullPointerException whenever I try to use the `LoginPresenter in my test code.

@RunWith(MockitoJUnitRunner.class)
@EBean
public class LoginPresenterTest {

    @Bean
    LoginPresenter loginPresenter;

    @Mock
    private LoginView loginView;

    @AfterInject
    void initLoginPresenter() {
        loginPresenter.setLoginView(loginView);
    }

    @Test
    public void whenUserNameIsEmptyShowErrorOnLoginClicked() throws Exception {
        when(loginView.getUserName()).thenReturn("");
        when(loginView.getPassword()).thenReturn("asdasd");
        loginPresenter.onLoginClicked();
        verify(loginView).setEmailFieldErrorMessage();
    }
}
Ripon answered 17/3, 2016 at 14:50 Comment(0)
K
2

AndroidAnnotations works by creating subclasses of the annotated classes, and adds boilerplate code in them. Then when you use your annotated classes, you will swap the generated classes in either implicitly (by injecting) or explicitly (by accessing a generated class, for example starting an annotated Activity).

So in this case to make it work, you should have run the annotation processing on the test class LoginPresenterTest, and run the test only on the generated LoginPresenterTest_ class. This can be done, but i suggest a cleaner way:

@RunWith(MockitoJUnitRunner.class)
public class LoginPresenterTest {

    private LoginPresenter loginPresenter;

    @Mock
    private LoginView loginView;

    @Before
    void setUp() {
        // mock or create a Context object
        loginPresenter = LoginPresenter_.getInstance_(context);
    }

    @Test
    public void whenUserNameIsEmptyShowErrorOnLoginClicked() throws Exception {
        when(loginView.getUserName()).thenReturn("");
        when(loginView.getPassword()).thenReturn("asdasd");
        loginPresenter.onLoginClicked();
        verify(loginView).setEmailFieldErrorMessage();
    }
}

So you have a normal test class, and you instantiate the generated bean by calling the generated factory method.

Kappa answered 17/3, 2016 at 15:3 Comment(11)
how do I gain access to a context object out there (in the LoginPresenterTest class) - sorry, its my first time doing unit testingRipon
I extended InstrumentationTestCase and tried getInstrumentation().getContext() in getInstance_(Context context) but I am getting a NPE on that lineRipon
I suggest reading the anwers here. But you should get the context that way. Actually what object was null, the instrumentation or its context?Kappa
I tried everything, I tried those too. No context. Is there another way to wait for the injection to pass before tests are run?Ripon
That context should be available. Are you sure you are running the test correctly on a device or emulator?Kappa
the only thing I can think of is that these tests are in a test folder and not an androidTest folder. Might that be the problem?Ripon
These should be in the androidTest folder. test is for unit tests running on the local machine.Kappa
Although I wanted these to be unit tests, the only way I was able to get a hold of the context was to make them instrumentation tests. What you suggested worked and now I dont get a null pointer exception :)Ripon
How do I use Robolectric in order to resolve the original problem in my questionRipon
You can pass Robolectric.application as a context, or you can an Activity and pass that. Or you can even just mock Context. :)Kappa
I don't think mocking the context will do any good, because the constructor of LoginPresenter_.getInstance(context) needs this context in order to gain access to resources, string files and other real stuff. I will try the Robolectric thing.Ripon

© 2022 - 2024 — McMap. All rights reserved.