I try to create a Robolectric test (3.0-rc02) for the following Activity:
public class NotificationActivity extends ActionBarActivity {
private NotificationFragment fragment;
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_notification);
}
}
The test is looking like this:
#Config(manifest = IConfig.MANIFEST_PATH, emulateSdk = IConfig.SDK_VERSION, reportSdk = IConfig.SDK_VERSION)
#RunWith(RobolectricTestRunner.class)
public class AbstractFragmentTest {
#Test
public void test() {
Robolectric.buildActivity(NotificationActivity.class).setup().visible();
}
where SDK_VERSION = 18.
When running the test, I get this error:
java.lang.NullPointerException
at org.robolectric.res.builder.DefaultPackageManager.getActivityInfo(DefaultPackageManager.java:164)
at org.robolectric.util.ActivityController.getActivityInfo(ActivityController.java:65)
at org.robolectric.util.ActivityController.attach(ActivityController.java:51)
at org.robolectric.util.ActivityController$1.run(ActivityController.java:121)
at org.robolectric.shadows.ShadowLooper.runPaused(ShadowLooper.java:309)
at org.robolectric.shadows.CoreShadowsAdapter$2.runPaused(CoreShadowsAdapter.java:47)
at org.robolectric.util.ActivityController.create(ActivityController.java:118)
at org.robolectric.util.ActivityController.create(ActivityController.java:129)
at org.robolectric.util.ActivityController.setup(ActivityController.java:210)
at com.viae.common.view.AbstractFragmentTest.test(AbstractFragmentTest.java:31)
Anyone knows what I'm doing wrong over here?
Issue solved
Using FragmentActivity in stead of ActionBarActivity did the trick to me.
My main logic is in a fragment (DialogFragment) inside the activity. Both activities are supporting the DialogFragment, so for my functional tests it doesn't matter which of the 2 parent activities I use.
ActionBarActivity is deprecated. Use AppCompatActivity instead
Related
I had an Activity that extended AppCompactActivity, and in onCreate method I setted the Toolbar using setSupportActionBar method in the usual way:
public class StepMasterActivity extends AppCompatActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_step_master);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);`
}
}
But now I have a ViewModel component and to share data between fragments that are the children of this activity and manages lifecycles I have to get this component in Activity and so I make this extend LifecycleActivity.
public class StepMasterActivity extends LifecycleActivity {
#Override
public class StepMasterActivity extends LifecycleActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_step_master);
// setToolbar();
SharedViewModel sharedViewModel = ViewModelProviders.of(this).get(SharedViewModel.class);
}
}
But I noticed that LifecycleActivity has nothing to do with AppCompatActivity neither FragmentActivity does.
public class LifecycleActivity extends FragmentActivity implements LifecycleRegistryOwner {
private final LifecycleRegistry mRegistry = new LifecycleRegistry(this);
public LifecycleActivity() {
}
public LifecycleRegistry getLifecycle() {
return this.mRegistry;
}
}
Am I doing something wrong?
UPDATE 2017-10-05: LifecycleActivity has been deprecated. If you use 26.1.0 or higher of support-fragment and appcompat-v7, both FragmentActivity and AppCompatActivity implement LifecycleOwner.
The original answer appears below for historical (and possibly hysterical) purposes.
Quoting the documentation:
Note: Since the Architecture Components are in alpha stage, Fragment and AppCompatActivity classes cannot implement it (because we cannot add a dependency from a stable component to an unstable API). Until Lifecycle is stable, LifecycleActivity and LifecycleFragment classes are provided for convenience. After the Lifecycles project is released, support library fragments and activities will implement the LifecycleOwner interface; LifecycleActivity and LifecycleFragment will be deprecated at that time.
LifecycleActivity is tied to FragmentActivity, not AppCompatActivity.
You should be able to create your own AppCompatLifecycleActivity as follows:
public class AppCompatLifecycleActivity extends AppCompatActivity implements LifecycleRegistryOwner {
private final LifecycleRegistry mRegistry = new LifecycleRegistry(this);
#Override
public LifecycleRegistry getLifecycle() {
return mRegistry;
}
}
The most recent support library revision 26.1.0 will allow you to use AppCompatActivity
Fragment and FragmentActivity (the base class for AppCompatActivity)
now implement the LifecycleOwner interface from Architecture
Components.
I have a BaseActivity which is an abstract activity and isn't registered in AndroidManifest. BaseActivity will call getPresenter in activity's lifecycle.
public abstract class BaseActivity extends AppCompatActivity{
public abstract Presenter getPresenter;
public abstract int getLayout();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayout());
getPresenter().attachView(this);
}
#Override
protected void onDestroy() {
super.onDestroy();
getPresenter().detachView();
}
}
I use ActivityTestRule to launch the BaseActivity, but the following error is shown.
java.lang.RuntimeException: Could not launch activity
How to test the getPresenter().attachView(this) and getPresenter().detachView() are called in correct activity's lifecycle?
I don't have quite big experience with Android Testing, especially unit testing, but I've already found this post, which may be useful for you:
Is it possible to test an Abstract activity with Robolectric
Also on Github page of Robolectric I'd found this: https://github.com/robolectric/robolectric/issues/1441
So all I can say according to your question, that yes you can test your abstract class, at least with Robolectric.
Read also: https://gualtierotesta.wordpress.com/2015/01/28/tutorial-java-abstract-classes-testing/
EDIT: Nowadays, Robolectric doesn't support directly API 23, but you can "downgrade" it in configuration of test class, like below:
#RunWith(RobolectricGradleTestRunner.class)
#Config(constants = BuildConfig.class, sdk = 21)
public class MainActivityTest {
MainActivity_ activity = Robolectric.setupActivity(MainActivity.class);
}
The error is shown because the BaseActivity isn't registered in Android Manifest. It seems that there are some solutions to add an activity in test package.
However, I finally choose another solution, delegate the activity's lifecycle to others.
This idea is mentioned in Mosby playbook.
http://hannesdorfmann.com/android/mosby-playbook/
New BaseActivity:
public abstract class BaseActivity extends AppCompatActivity implements BaseMvpView, DelegateCallback{
private ActivityMvpDelegate activityDelegate;
protected ActivityMvpDelegate getActivityDelegate() {
if (activityDelegate == null) {
activityDelegate = createActivityDelegate();
}
return activityDelegate;
}
protected ActivityMvpDelegate createActivityDelegate() {
return new ActivityMvpDelegateImpl(this, this);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivityDelegate().onCreate(savedInstanceState);
}
#Override
protected void onDestroy() {
super.onDestroy();
getActivityDelegate().onDestroy();
}
}
Finally, I can test the delegate class without activity's lifecycle.
Working on my first Android project and needed to add a settings activity for program settings. I created a MainActivity with a single button to test the settings activity.
I created the default SettingsActivity from Android Studio (didn't change anything).
Tried calling the Settings Activity via a click on the button from the main activity. Getting the following error:
Cannot Resolve Constructor 'Intent(android.view.View.OnClickListener...
The Settings Activity is called ProgramSettingsActivity and is the default code that AndroidStudio creates... for now. The beginning of it reads...
public class ProgramSettingsActivity extends PreferenceActivity {
private static final boolean ALWAYS_SIMPLE_PREFS = false;
#Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
setupSimplePreferencesScreen();
}
...
The code in the mainActivity is as follows:
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button=(Button)findViewById(R.id.myButton);
button.setOnClickListener(new Button.OnClickListener(){
public void onClick(View v) {
Intent i = new Intent(this,ProgramSettingsActivity.class);
startActivity(i);
}
});
}
...
Not sure where to go from here... I am obviously missing something obvious.
Thanks for any help....
Use Intent i = new Intent(MainActivity.this,ProgramSettingsActivity.class);
Inside anonymous class new Button.OnClickListener(){} this refers to the instance of this class, not its enclosing MainActivity class. That's how it happens.
I'm trying to create a simple Android Activity test that checks that a new Activity is started when a button is clicked. The code:
public class LoginActivityTest extends ActivityInstrumentationTestCase2<LoginActivity> {
public LoginActivityTest() {
super(LoginActivity.class);
}
#Override
protected void setUp() throws Exception {
super.setUp();
setActivityInitialTouchMode(false);
mActivity = getActivity();
mLoginButton = (Button) mActivity.findViewById(R.id.login_button);
mSkipButton = (Button) mActivity.findViewById(R.id.skip_button);
}
{...}
public void testSkipButton() {
Instrumentation.ActivityMonitor monitor =
getInstrumentation().addMonitor(
"com.mycompany.myproject.view.QuestionsActivity", null, false);
mActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
mSkipButton.requestFocus();
mSkipButton.performClick();
}
});
QuestionsActivity nextActivity =
(QuestionsActivity) getInstrumentation().waitForMonitorWithTimeout(monitor, 20);
assertNotNull(nextActivity);
nextActivity.finish();
}
private LoginActivity mActivity;
private Button mLoginButton;
private Button mSkipButton;
}
When I reach waitForMonitorWithTimeout() a NoClassDefFoundException is raised.
It's important to take into account that QuestionsActivity (the activity that should be launched) is a FragmentActivity, not an Activity, but FragmentActivity inherits from Activity, so I really don't understand what's happening there. Maybe InstrumentationTest cannot deal with Fragments or FragmentActivities.
mActivity is the current Activity that it's being tested, and it's a pure Activity.
Please, help!
The issue was that I added a reference to the support library from the testing project. See this link:
FragmentActivity can not be tested via ActivityInstrumentationTestCase2
Now tests work fine :)
I am writing junit test cases for an android project. I wrote junit for activity classes but I dont know how to write test cases for other classes that doesn't inherit from activity. Also, how can I link these classes (activity and non activity classes)?
examples:
public class A extends Activity{
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.someLayout);
B objectB = new B();
pbjectB.getString();
}
}
public class B{
public b(){
}
public String getString(){
return anyString;
}
}
In this example I am able to write junit test cases for class A but I am confused for class B.
For non-Activity classes, you can use standard junit 3 test classes. So make your test for B extend from junit.framework.TestCase
import junit.framework.TestCase;
public class BTest extends TestCase {
public void testGetStringIsNotNull() {
B subject = new B();
assertNotNull(subject.getString());
}
}