Roboelectric : How to resolve depended object - android

I have one activity and I am testing one method of it using Roboelectic Framework but I am getting null pointer exception.
following is test code
#Test
public void processTest() throws Exception {
MyActivity activity = Robolectric.setupActivity(MyActivity.class);
activity.setValue();
tView = (TextView) activity.findViewById(R.id.text);
assertEquals(“1”,tView.getText().toString());
}
following is setValue method of activity
public class MyActivity extends AppCompatActivity {
TextView tView;
#Inject Student s;
#Override
public void onCreate() {
tView = (TextView) activity.findViewById(R.id.text);
}
public void setValue() {
String id = s.getId();
tView.setText(S);
}
I am getting null pointer error on String id = s.getId(); line when I run test
I know s is null. but my question how to make it available in Test using Roboelectric without using Mokito ?

Are you sure you pasted your code exactly as it run? onCreate should have parameter of Bundle, and the activity has not called setConentView before you do findViewById, and some other problems....
In general, The activity constructed by Robolectric.setupActivity should be ready for use in the rest of the test case, and onCreate(Bundle) should have been executed before return.

Related

Android UI testing with mocked dependencies

I have an activity in which I provide a button. Clicking on the button invokes a method in a data provider class and based on the return value of the method I make UI changes. Now I want to write an instrumented test where I perform click() in the button but avoid actually calling the method in the data provider class. Instead I want to return a desired value from the method and then check if the UI was modified accordingly.
MyActivity
#Override
public void onCreate(final Bundle savedInstanceState) {
mActionButton.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(final View v) {
boolean result = dataProvider.getResult();
if(result) {
mSuccessTextView.setVisibility(View.VISIBLE);
}
}
});
}
Here, on button click, a call is made to DataProvider#getResult and the result from this method is stored in result. If the result is true a TextView mSuccessTextView, previously GONE, is now made VISIBLE.
The problem here is DataProvider#getResult deals with a lot of external components that would make testing impossible. So what I want to do is use a mocked instance of DataProvider so that I can get getResult to return a desired value and then check the visibility of mSuccessTextView. This is what I tried :
MyActivityTest.java
#RunWith(AndroidJUnit4.class)
public class MyActivityTest {
private DataProvider mDataProvider;
#Rule
public IntentsTestRule<MyActivity> mIntentRule =
new IntentsTestRule<>(MyClientActivity.class);
#Before
public void setUp() {
mDataProvider = mock(DataProvider.class);
}
#Test
public void testResultSuccess() {
boolean result = true;
when(mDataProvider.getResult()).thenReturn(result);
onView(withId(R.id.action_button)).perform(click());
onView(withId(R.id.success_text_view)).check((ViewAssertion) isDisplayed());
}
}
Doing the above generates the following error :
org.mockito.exceptions.base.MockitoException:
Mockito cannot mock this class: class com.domain.myapp.DataProvider.
Mockito can only mock non-private & non-final classes.
If you're not sure why you're getting this error, please report to the mailing list.
Underlying exception : java.lang.UnsupportedOperationException: Cannot define class using reflection
.
.
.
Caused by: java.lang.UnsupportedOperationException: Cannot define class using reflection
.
.
.
Caused by: java.lang.IllegalStateException: This JVM's version string does not seem to be valid: 0
.
.
.
Even if you could mock DataProvider, it would not help you because you are not injecting its instance into MyClientActivity during your test. Reasons for not able to mock DataProvider are unknown, pls provide the class.

ArgumentCaptor captures wrong class

SOLVED: Even if the getValue() on the Argument Captor shows you this, it is normal. To be honest I was expecting to see and instance on the OnLoginWithEmailCallback interface here. The problem on my side was related to a method call on mView which was generating a NPE. Works like a charm now.
ORIGINAL PROBLEM:
I am implementing my first unit test using Mockito in my MVP app and I need to mock the behaviour of a callback when the user is logging in. I am using Firebase to handle the authentication.
I followed a very good tutorial from here : https://fernandocejas.com/2014/04/08/unit-testing-asynchronous-methods-with-mockito/.
I am calling method on class under test. This method calls another one on the Auth Presenter which does the actual work
mPresenter.performLoginWithEmail(EMAIL, PASSWORD);
Then I am verifying that an underlining method in the Auth Presenter class was called. I try to capture the callback interface.
verify(mAuthPresenter, times(1)).login(mOnLoginWithEmailCallbackArgumentCaptor.capture(),
eq(EMAIL), eq(PASSWORD));
The problem is that getValue() from the Argument Captor returns an instance of the mPresenter (class under test) instead of the OnLoginWithEmailCallback interface class. Therefore I get an NPE.
mOnLoginWithEmailCallbackArgumentCaptor.getValue().onLoginWithEmailSuccess();
Here is the complete test class:
#RunWith(MockitoJUnitRunner.class)
public class LoginPresenterTest {
private static String EMAIL = "test#rmail.com";
private static String PASSWORD = "1234";
//class under test
private LoginPresenter mPresenter;
#Mock
AuthPresenter mAuthPresenter;
#Captor
private ArgumentCaptor<OnLoginWithEmailCallback> mOnLoginWithEmailCallbackArgumentCaptor;
#Mock
ILoginActivityView mView;
#Before
public void setupLoginPresenter() {
MockitoAnnotations.initMocks(this);
// Get a reference to the class under test
mPresenter = new LoginPresenter(mView, mAuthPresenter);
}
#Test
public void performLoginWithEmail() {
mPresenter.performLoginWithEmail(EMAIL, PASSWORD);
//wanting to have control over the callback object. therefore call capture to then call methods on the interface
verify(mAuthPresenter, times(1)).login(mOnLoginWithEmailCallbackArgumentCaptor.capture(),
eq(EMAIL), eq(PASSWORD));
mOnLoginWithEmailCallbackArgumentCaptor.getValue().onLoginWithEmailSuccess();
InOrder inOrder = Mockito.inOrder(mView);
inOrder.verify(mView).goToMap();
inOrder.verify(mView).hideProgressBar();
}
}
EDIT: This is the call to mAuthPresenter.login:
SOLVED: getLoginActivityView() was causing an NPE
public void performLoginWithEmail(String email, String password) {
mAuthPresenter.login(new OnLoginWithEmailCallback() {
#Override
public void onLoginWithEmailSuccess() {
getLoginActivityView().goToMap();
getLoginActivityView().hideProgressBar();
}
#Override
public void onLoginWithEmailFailed(String error) {
getLoginActivityView().hideProgressBar();
getLoginActivityView().showToast(error);
}
}, email, password);
}
I also tried using using doAnswer from Mockito:
doAnswer(new Answer() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
((OnLoginWithEmailCallback)invocation.getArguments()[0]).onLoginWithEmailSuccess();
return null;
}
}).when(mAuthPresenter).login(
any(OnLoginWithEmailCallback.class, EMAIL, PASSWORD));
Still, invocation.getArguments() return an instance of the class under test (LoginPresenter), so the same problem as before. Can you help me?
The problem was that i got confused by the Argument Captor which returned me an instance of the caller class (LoginPresenter). The problem in my case was with a method inside the anonymouse class OnLoginWithEmailCallback() which was throwing an NPE.

How to test new Button()?

I have some ButtonCreator.class that create Android UI element via models. Have any ideas how to test it?
public class ButtonCreator {
Button createWidget(ButtonComponent buttonComponent, Context context) {
Button button = new Button(context);
button.setText(buttonComponent.getText());
return button;
}
}
I wrote a test:
#RunWith(PowerMockRunner.class)
#PrepareForTest({Button.class, Context.class, ButtonCreator.class})
public class CreaterButtonTest {
#Mock
Context mContext;
#Mock
Button mButton;
#Test
public void createWidget() {
PowerMockito.whenNew(Button.class).withAnyArguments(). thenReturn(mButton);
ButtonCreator buttonCreator = new ButtonCreator();
ButtonComponent buttonComponent = new ButtonComponent();
buttonComponent.setText("someText");
Button buttonActual = buttonCreator.createWidget(buttonComponent, mContext);
assertThat(buttonActual.getText(), is("someText"));
}
}
But in buttonActual.getText() I have a null. Help it solve please.
Important thing to understand about mocks, is that they will never function as your original object, unless you specify the behavior.
In your case:
The Mocked button will receive the call of setText(), but because it's a Mocked object, it does not have any internal state to store the text string, so it will correctly return null.
You can specify the behavior to return a string with something like:
when(mButton.getText()).thenReturn("mytext");
But this is not so optional, because in the end you are testing how the mock framework is working.
In these cases, you should use the verify method. Every mock object records all method calls during it's lifetime.
verify(mButton, times(1)).setText("someText"));
With this you are making sure that the exact method calls are happening.

Mock activity class member before onCreate

I am trying to test an activity using robolectric 3.3.2.
Want to mock and activity's member initialization as the direct call results in NPE.
ActivityController<MyActivity> activityController =
Robolectric.buildActivity(MyActivity.class);
mTestActivity = activityController.get();
Mockito.when(mTestActivity.getCountry()).thenReturn("xxxx");
activityController.setup();
Tried out above code, but the setup.() (oncreate) ignores the mock of
getCountry method and invokes the definition from activity.
Is there a way to achieve this?
It will not work like this even if you use spies (#Spy, Mockito.spy()).
You should use stub:
public class MyActivityTest{
public static class StubMyActivity extends MyActivity {
Country getCountry() {
return someSpecialCountry;
}
}
#Before
public void setUp(){
ActivityController<StubMyActivity> activityController =
Robolectric.buildActivity(StubMyActivity.class);
mTestActivity = activityController.setup().get();
}
}

Using ActivityInstrumentationTestCase2 cannot seem to test ListView contents

I have the following test code. I am trying to test whether a list is being populated from SQLite database.
public class ViewIssuesActivityTest extends BaseActivityTest<ViewIssuesActivity>{
private List<Issue> issues;
public ViewIssuesActivityTest() {
super(ViewIssuesActivity.class);
}
public void setUp() throws Exception {
super.setUp();
issues = new ArrayList<Issue>();
issues.add(new Issue("Trial","Desc","Location","ImagePath"));
IssueRepository issueRepository = mock(IssueRepository.class);
doAnswer(new Answer<Object>() {
#Override
public List<Issue> answer(InvocationOnMock invocation) throws Throwable {
return issues;
}
}).when(issueRepository).getIssues();
activity.setIssueRepository(issueRepository);
}
public void testNumberOfIssuesRecorded() {
ListView listView = (ListView) activity.findViewById(android.R.id.list);
assertEquals(1, listView.getCount());
}
}
My BaseActivityTest code is:
public class BaseActivityTest<T extends Activity> extends ActivityInstrumentationTestCase2<T> {
protected T activity;
public BaseActivityTest(Class<T> activityClass) {
super(activityClass);
}
#Override
protected void setUp() throws Exception {
activity = getActivity();
}
}
My ViewIssuesActivity is as follows:
public class ViewIssuesActivity extends ListActivity{
private IssueRepository issueRepository;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(com.change.kranti.R.layout.issues);
issueRepository = new IssueRepository(getApplicationContext());
List<Issue> issues = new ArrayList<Issue>();
issues = issueRepository.getIssues();
ArrayAdapter<Issue> adapter = new ArrayAdapter<Issue>(this,
android.R.layout.simple_list_item_1, issues);
setListAdapter(adapter);
}
}
The issue is I get error: expected<1> got <0>
I think the issue is the onCreate method is getting called before the issueRepository is created.
I want to mock the IssueRepository and test my list getting populated.
What is the issue with my code or is there a better way to test this functionality.
Any help will be appreciated.
You are right. The issue is that the onCreate method gets called before the mock issueRepository gets injected. When you call getActivity in the context of an ActivityInstrumentationTestCase2, all the usual life cycle methods get called (i.e., onCreate, onStart, and onResume). When your test code calls setIssueRepository, it's already too late, the issues have been fetched from the repository.
The best solution I know in this kind of case is to use dependency injection, whether by hand or using a DI framework.
It's not clear what you're trying to do.
You can't run two tests simultaneously against the same class. The ActivityInstrumentationTestCase2 class is essentially a controller and wrapper for the Activity under test, but it can only control a single Activity, and you can't control the same Activity with multiple instances of ActivityInstrumentationTestCase2.
To set up an app for test-driven development, try to use as many POJOs (Plain Old Java Objects) as you can. Limit the Activity testing to stuff such as sending Intents or integration between POJOs that you can mock.
ActivityInstrumentationTestCase2 is a JUnit TestCase subclass, but it has limitations.

Categories

Resources