I have a little problem figuring out how to test my Activity using Robolectric 2.2. I am probably not correctly setting up the lifecycle or the entire test...
In my Activity, I have a Otto producer like this:
#Produce public GetAlarmList produceAlarmList() {
Log.v(this, "Producing GetAlarmList event.");
return new GetAlarmList(mAlarmList);
}
The following is my test.
#RunWith(RobolectricTestRunner.class)
public class GLarmListFragmentTests {
//GLarmMain mActivity;
ActivityController<GLarmMain> mController;
#Before
public void setUp() throws Exception {
mController = Robolectric.buildActivity(GLarmMain.class);
}
#After
public void tearDown() throws Exception {
mController = mController.destroy();
}
#Test
public void shouldHaveListFragment() {
GLarmMain activity = mController.create().start().resume().visible().get();
GLarmListFragment listFragment = (GLarmListFragment) activity.getSupportFragmentManager().findFragmentById(R.id.main_list_frag);
assertNotNull(listFragment);
}
#Test
public void shouldHaveListAdapter() {
GLarmMain activity = mController.create().start().resume().visible().get();
GLarmListFragment listFragment = (GLarmListFragment) activity.getSupportFragmentManager().findFragmentById(R.id.main_list_frag);
FuncAlarmListAdapter listAdapter = (FuncAlarmListAdapter)listFragment.getListAdapter();
assertNotNull(listAdapter);
}
}
Every time I launch it, I receive:
java.lang.IllegalArgumentException: Producer method for type class ar.android.app.glarm.events.GetAlarmList found on type class ar.android.app.glarm.ui.GLarmMain, but already registered by type class ar.android.app.glarm.ui.GLarmMain.
at com.squareup.otto.Bus.register(Bus.java:198)
at ar.android.app.glarm.ui.GLarmMain.onResume(GLarmMain.java:461)
at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1184)
at android.app.Activity.performResume(Activity.java:5082)
at org.fest.reflect.method.Invoker.invoke(Invoker.java:112)
at org.robolectric.util.ActivityController$6.run(ActivityController.java:216)
at org.robolectric.shadows.ShadowLooper.runPaused(ShadowLooper.java:256)
at org.robolectric.util.ActivityController.invokeWhilePaused(ActivityController.java:214)
at org.robolectric.util.ActivityController.resume(ActivityController.java:152)
at ar.android.app.glarm.test.GLarmListFragmentTests.shouldHaveListAdapter(GLarmListFragmentTests.java:51)
Does someone have the same issue? How can I solve it?
Found the problem, leaving it here for reference.
I was not handling the pause()/onPause() life-cycle step and therefore never calling:
#Override
protected void onPause() {
super.onPause();
Log.v(this, ".onPause()");
mBus.unregister(this); // unregisters.
}
Changing the above code as following solved:
#After
public void tearDown() throws Exception {
mController = mController.pause().stop().destroy();
}
Related
I apologise if this is a bit too vague here, but I'm not allowed to post my whole actual code. All I can say is I have a problem running this test as a part of ./gradlew connectedAndroidTest
#RunWith(AndroidJUnit4.class)
#LargeTest
public class MobileAppSanityTest extends AbstractEspressoTest {
#Rule
public ActivityTestRule<MainActivity> mActivityRule =
new ClearPreferencesActivityTestRule<>(MainActivity.class, getFiles());
#Override
protected Context getContext() {
return mActivityRule.getActivity();
}
#BeforeClass
public static void beforeAll() {
RoboGuice.Util.reset();
}
#Test
public void test_SingleUserFlow() {
navigateSplashScreen();
logIn();
doSomethingElse();
}
}
What happens here is that when I run this test class on its own - it runs fine, but when I run it as a part of 'connectedAndroidTest' the activity is stopped right after 'navigateSplashScreen' and login cannot be performed.
Error I get is:
java.lang.RuntimeException: No activities found. Did you t to launch the activity by calling getActivity() or startActivitySync or similar?
I'm quite new to Espresso and Android in general, so it's a bit hard to wrap my head around this. Please let me know if you need more information. I'll try to provide it out if that's the case.
a jUnit TestCase looks differently; think one can only use Espresso in there.
#RunWith(AndroidJUnit4.class)
public class MainActivityTest extends TestCase {
/** Log Tag */
private static final String LOG_TAG = MainActivityTest.class.getSimpleName();
/** the Activity of the Target application */
private MainActivity mActivity;
#Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<MainActivity>(MainActivity.class) {
};
#Override
public void setUp() throws Exception {
super.setUp();
}
/* obtaining the context from the ActivityTestRule */
#Before
public void setUpTest() {
this.mActivity = this.mActivityRule.getActivity();
}
/* add Espresso code eg. here */
#Test
#UiThreadTest
public void navigateSplashScreen() {
}
#Override
public void tearDown() throws Exception {
super.tearDown();
}
}
I'm trying to test a Fragment that has in interface that must be implemented by the hosting Activity and gets casted to that specific interface's type through onAttach().
Problem: I'm not sure how to implement the necessary interface methods within an Android Unit Test or if it's even necessary to do so. Surprisingly, I haven't found any posts or forums that address this issue.
Test:
public class FragmentTest {
private ActivityForUnitTesting fragmentHostActivity;
private ExampleFragment fragmentToTest;
#Rule
public ActivityTestRule activityTestRule = new ActivityTestRule<>(ActivityForUnitTesting.class);
#Before
public void setUp() {
fragmentHostActivity = (ActivityForUnitTesting) activityTestRule.getActivity();
fragmentManager = fragmentHostActivity.getSupportFragmentManager();
fragmentToTest = new ExampleFragment();
}
#Test
public void testExample() {
fragmentManager.beginTransaction()
.replace(R.id.frame_layout_container, fragmentToTest)
.commit();
}
}
Fragment:
public class ExampleFragment extends Fragment {
private ExampleFragmentListener exampleFragmentListener;
...
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
exampleFragmentListener = (ExampleFragmentListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement ExampleFragmentListener");
}
}
...
}
but when I try running a simple test I get:
java.lang.ClassCastException: com.example.package.ActivityForUnitTesting#1234567 must implement ExampleFragmentListener
at com.example.package.ExampleFragment.onAttach(ExampleFragment.java:)
I know that the issue is that my Unit Test ActivityForUnitTesting object does not implement the required interface methods. My question is, how do I safely implement those methods within my Unit Test. I haven't had any luck finding a similar question or a solid example.
I didn't find a solution to this, but I did find a "workaround". Instead of using onAttach(), explicitly set your listener through a public method.
public class ExampleFragment extends Fragment {
private ExampleFragmentListener exampleFragmentListener;
...
//#Override
//public void onAttach(Context context) {
// super.onAttach(context);
// try {
// exampleFragmentListener = (ExampleFragmentListener) context;
// } catch (ClassCastException e) {
// throw new ClassCastException(context.toString() + " must implement ExampleFragmentListener");
// }
//}
public void setExampleFragmentListener(ExampleFragmentListener exampleFragmentListener) {
this.exampleFragmentListener = exampleFragmentListener;
}
...
}
then, you should already have ExampleFragmentListener implemented in your host Activity. Just call
setExampleFragmentListener(ActivityOrClassThatImplementsExampleFragmentListener)
from wherever you perform your Activity setup. As a result, the test shouldn't complain about unimplemented methods.
I am new with tests.
I have something like next code and wish to cover it with unitTests using the Mockito:
public void doSomeJob(){
//some code before
getMvpView().execute(getObservable());
//some code after
}
private Observable<Boolean> getObservable(){
return Observable.create(new ObservableOnSubscribe<Boolean>() {
#Override
public void subscribe(#NonNull ObservableEmitter<Boolean> e) throws Exception {
Thread.sleep(5000);
e.onNext(true);
e.onComplete();
}
});
}
so questions:
how correct write test for getMvpView().execute(getObservable()); using Mokito?
how can i verify result of getObservable()?
If your private method is not a part of the interface, i.e. cannot be reached from outside the class, it's not something you should test (presumably it's not, since it's private). Mockito in turn doesn't provide mocking of private methods. Thereby you either need to change your interface (make this data available outside) or leave it without testing.
What you should test is the effect of calling the public methods of your class under test. If you do so you will be able to freely refactor the implementation details later, and your tests will still verify that your class works as expected.
I suppose that your code is part of a presenter implementation and the getMvpView() method returns a view interface:
public class MvpPresenterImpl {
private MvpView view;
public void doSomeJob(){
//some code before
getMvpView().execute(getObservable());
//some code after
}
public void attachView(MvpView view) {
this.view = view;
}
private MvpView getMvpView() {
return view;
}
private Observable<Boolean> getObservable(){
return Observable.create(new ObservableOnSubscribe<Boolean>() {
#Override
public void subscribe(#NonNull ObservableEmitter<Boolean> e) throws Exception {
Thread.sleep(5000);
e.onNext(true);
e.onComplete();
}
});
}
}
You can test the effect of doSomeJob() like so:
public class MvpPresenterImplTest {
private MvpPresenterImpl presenter;
private MvpView mockView;
#Before
public void setUp() throws Exception {
// Create a mock view instance so that we can verify method calls on it
mockView = mock(MvpView.class);
// Create our object under test, and set it up with the mock view
presenter = new MvpPresenterImpl();
presenter.attachView(mockView);
}
#Test
public void doSomeJob_callsExecuteOnViewWithCorrectObserver() throws Exception {
// What we want to test is the effect of invoking a public method.
presenter.doSomeJob();
// Verify that the execute method has been called by your class
// under test, and save the parameter for later.
ArgumentCaptor<Observable<Boolean>> paramCaptor =
ArgumentCaptor.<Observable<Boolean>>forClass((Class)Observable.class);
verify(mockView).execute(paramCaptor.capture());
// Get the actual observable that the execute method was called with.
Observable<Boolean> param = paramCaptor.getValue();
// Get a test observer so that we can check what our Observable emits
// (TestObserver is a built-in feature of RxJava, not Mockito.)
TestObserver<Boolean> test = param.test();
// Assert that the Observable behaves as expected
test.assertComplete();
test.assertResult(true);
}
}
I've tried everything already. The startActivity are not receiving the intent. Here's the code:
public class ColaboradorTeste extends ActivityUnitTestCase<ColaboradorMainActivity> {
private UserDAO userDAO;
private ColaboradorMainActivity activity;
public ColaboradorTeste() {
super(ColaboradorMainActivity.class);
}
#Override
protected void setUp() throws Exception{
super.setUp();
startActivity(new Intent(getInstrumentation().getTargetContext(), ColaboradorMainActivity.class), null, null);
activity = (ColaboradorMainActivity)getActivity().getApplicationContext();
userDAO = new UserDAO(activity);
}
public void testBase() throws Exception{
...
}
}
Here's the error:
java.lang.NullPointerException
at br.fsw.seatafmobile.ColaboradorTeste.setUp(ColaboradorTeste.java:23)
I know the problem is in the startActivity, but I really don't know how to solve it.
If anyone could help I'll really appreciate.
I finally found the solution!
My test is using a database created on the app. So I had to change the Build Variants from Unit Test to Android Instrumentation Tests.
I'm trying to spy on an Activity but it gives me the following exception:
java.lang.AbstractMethodError: abstract method "boolean org.mockito.internal.invocation.AbstractAwareMethod.isAbstract()"
at org.mockito.internal.invocation.InvocationImpl.callRealMethod(InvocationImpl.java:109)
at org.mockito.internal.stubbing.answers.CallsRealMethods.answer(CallsRealMethods.java:41)
at org.mockito.internal.handler.MockHandlerImpl.handle(MockHandlerImpl.java:93)
at org.mockito.internal.handler.NullResultGuardian.handle(NullResultGuardian.java:29)
at org.mockito.internal.handler.InvocationNotifierHandler.handle(InvocationNotifierHandler.java:38)
at com.google.dexmaker.mockito.InvocationHandlerAdapter.invoke(InvocationHandlerAdapter.java:49)
at WaitForCardActivity_Proxy.isRegisterActivated(WaitForCardActivity_Proxy.generated)
The code is as follows:
#RunWith(AndroidJUnit4.class)
public class WaitForCardActivityTests extends ActivityInstrumentationTestCase2<WaitForCardActivity> {
#Before
#Override
public void setUp() throws Exception {
super.setUp();
injectInstrumentation(InstrumentationRegistry.getInstrumentation());
waitForCardActivity = spy(getActivity());
when(waitForCardActivity.isRegisterActivated()).thenReturn(true);
}
...
}
Activity:
public class WaitForCardActivity extends Activity {
...
public boolean isRegisterActivated() {
...
}
}
Also note: I cannot use Robolectric because I'm using Ciphers which give me problems when running with Robolectric.
Spy Activity in following way:
Activity activity = spy(Activity.class);
Update:
And in your case:
WaitForCardActivity activity = spy(WaitForCardActivity.class);
when(activity.isRegisterActivated()).thenReturn(true);
assertTrue(activity.isRegisterActivated());