This is the class of which I want to test EditText. But when I try to assign that EditText field in the Test clas it shows an null pointer exception. I have omitted other use less code for the problem
public class LogInActivity extends ActionBarActivity {
private Button signUpButton;
private Button logInButton;
private Intent signUpChoiceIntent;
private Intent homeActivityIntent;
private String username;
private String password;
private EditText usernameTextField;
private EditText passwordTextField;
private HumLogController humLogController;
private String error;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_log_in);
humLogController = (HumLogController) getIntent().getSerializableExtra("controllerObject");
setIntentAndButton();
}
private void setAndCheckFields(){
/** I want to test this, (view with id:- logInUsernameField ) which is working fine in practice, but not passing the test. When I try to call the same id in Test class with instance of this class, it gives a null pointer exception. */
usernameTextField = (EditText) findViewById(R.id.logInUsernameField);
}
This is the test class where I am testing for the EditText field, but giving a null pointer exception
public class LogInActivityInstrumentTest extends InstrumentationTestCase{
LogInActivity logInActivity;
#Override
protected void setUp() throws Exception{
super.setUp();
logInActivity = new LogInActivity();
}
public void testUsernameTextViewNullTest(){
// The line below is line 23. Which is giving null pointer Exception...?
EditText text = (EditText) logInActivity.findViewById(R.id.logInUsernameField);
assertNotNull(text);
}
#Override
protected void tearDown() throws Exception{
super.tearDown();
}
}
The log cat is given below.
java.lang.NullPointerException
at android.app.Activity.findViewById(Activity.java:1853)
at com.example.praduman.humlog.tests.LogInActivityInstrumentTest.testUsernameTextViewNullTest(LogInActivityInstrumentTest.java:23)
at java.lang.reflect.Method.invokeNative(Native Method)
at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:214)
at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:199)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:554)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1701)
You cannot create an Activity simply calling its constructor as you did. In a test context, you need some kind of instrumentation to allow everything to work properly. Try to take a look at Espresso (for in device tests) or even Robolectric (for JVM tests).
You can not create an instance of your activity using the constructor like ou have done in the above code.
Try changing the following in your LogInActivityInstrumentTest
public class LogInActivityInstrumentTest extends InstrumentationTestCase<LogInActivity>{
public LogInActivityInstrumentTest() {
super(LogInActivity.class);
}
#Override
protected void setUp() throws Exception{
super.setUp();
logInActivity = new getActivity();
}
}
The documentation for running tests can be found here, on developer.android.com.
In the code you have posted - simple creating the Activty using its constructor does not run it through the lifecycle that an activity expects. The reason for your NullPointerException is the fact that onCreate has not been run, meaning that you are trying to look up a view before you have called setContentView(), therefore, the view really is null.
Related
I'm trying to make some utils functions to use in a bigger app later(download file from url, upload file to url etc)
So in MainActivity I have only 2 buttons that on click call static methods from Utils class.
However, I want on MainActivity to have some indicators of how things working on download/upload methods(connecting, connection success/fail, percent of download etc) so I put on MainActivity a TextView that will show that. I made an interface ICallback that contains void setConnectionStatus(String status) and from Utils class I use this to send to MainActivity the status.
Here are some parts of the code :
public class MainActivity extends AppCompatActivity implements ICallback {
Button btnDownloadDB, btnUploadDB, btnUploadPics;
TextView txtStatus;
ProgressBar pb;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Initialize stuffs
initViews();
//Setting listeners
btnDownloadDB.setOnClickListener(v -> {
txtStatus.setText(R.string.connecting);
pb.setVisibility(View.VISIBLE);
Utils.downloadFile(DOWNLOAD_DB, DB_FILE_NAME);
});
}
#Override
public void setConnectionStatus(String status) {
Log.d("MIHAI", status);
txtStatus.setText(status);
}
The interface :
public interface ICallback {
void setConnectionStatus(String status); }
And the Utils class :
public class Utils {
static ICallback callback= new MainActivity();
public static void downloadFile(String downloadURL, String fileName) {
IFileTransferClient client = ServiceGenerator.createService(IFileTransferClient.class);
Call<ResponseBody> responseBodyCall = client.downloadFile(downloadURL);
responseBodyCall.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.d("MIHAI", "connection ok");
callback.setConnectionStatus("Connection successful");
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.d("MIHAI", "err...fail");
callback.setConnectionStatus("Connection failed. Check internet connection.");
}
});
}
The problem appear on MainActivity, when I try to set text of the txtStatus TextView getting a null reference error even if the txtStatus is initialized on initViews() method.
The Logs are working fine so I get the right status in MainActivity. I tried to initialize the TextView again in that function before seting the text and im getting : "java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference
at android.content.ContextWrapper.getApplicationInfo(ContextWrapper.java:183)"
Is there any chance to make this work?
Thank you for reading.
Kind regards,
Mihai
There are multiple problems with your solution but the main one is this line:
static ICallback callback= new MainActivity();
First of all, never hold a static reference to Activity, Fragment, Context or any Context related classes. These classes are either bound to a Context or represent the Context itself. You may leak memory this way. But that is the other problem.
What is the actual problem in your code is that new MainActivity() in Utils class creates an absolutely different instance of MainActivity that has nothing to do with MainActivity that is responsible for displaying your UI in the runtime.
What you should do instead is pass an instance of ICallback to the function as an argument:
public static void downloadFile(String downloadURL, String fileName, ICallback callback) {
...
}
And remove static ICallback callback= new MainActivity();.
Note: when you pass a callback object to a function make sure when it is called your Activity is not in a finished state.
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 6 years ago.
Problem: (All info below) I've set up the app to get text from EditText component and add it into a LinkedList once the button is pressed.
Tests: Testing for getText.toString() was successful (so u_in had a usable string) and I also tried to implement a simple array within my MainActivity and perform the same function which worked perfectly. Although I keep getting a NullPointerException with the database class and LinkedList.
I've also tried adding a null check in the OnClick anonymous method, but still got the same error.
Testing using physical device and running Android Studio 2.1
Main class:
public class MainActivity extends AppCompatActivity {
public Data_base db;
public String u_in;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
check_button();
}
public void check_button() {
Button add_button = (Button) findViewById(R.id.button);
final EditText etext = (EditText) findViewById(R.id.editText);
add_button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v){
u_in = etext.getText().toString();
db.add(u_in,0);
}
});
}
Input class(Initializes information):
public class Input {
public String input;
public int age;
public Input (String input, int age) {
this.input = input;
this.age = age;
}
}
Database class (where I have my linkedlist and methods to manipulate it):
public class Data_base {
public LinkedList<Input> user_in = new LinkedList<>();
public void add(String in, int age) {
user_in.add(new Input(in, age));
}
public LinkedList<Input> getList() {
return user_in;
}
}
Error:
05-02 22:51:59.202 2813-2813/com.example.user.test_app E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.user.test_app, PID: 2813
java.lang.NullPointerException: Attempt to invoke virtual method 'void com.example.user.test_app.Data_base.add(java.lang.String, int)' on a null object reference
at com.example.user.test_app.MainActivity$1.onClick(MainActivity.java:33)
at android.view.View.performClick(View.java:5155)
at android.view.View$PerformClick.run(View.java:20747)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:5832)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)
In your MainActivity you're using db.add without instantiating db first.
Edit Addressing Comment:
Not sure exactly what you're doing with the age variable, but generally there are some ways to programatically get the same variable from an entire collection:
1) Get the value while iterating over the original collection:
//Somewhere in your business logic:
for(Input i : db) {
String text = i.getInput(); //Your class should have getters and setters
//Do something with the text variable
}
2) Make a List<String> using a static method in your Input class:
public static List<Input> stringsFromInputs(List<Input> inputs) {
List<String> ret = new ArrayList<String>();
for(Input i : inputs)
ret.add(i.getInput()); //Again, your class should have getters/setters
return ret;
}
3) If you're not going to use such a static method often, then perform this logic where needed in the code itself (which you seem to want to avoid).
Hope this helps some.
In onCreate before calling check_button() add:
db = new Data_base();
Create Data_base object first in MainActivity the try to do operation with add method
I am trying to get id of an image by using following code.
public class MovieActivity extends ActionBarActivity {
private Context con;
String name = "test";
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
con = MovieActivity.this;
setContentView(R.layout.activity_movie);
}
public void updateScreen(){
int imageResource = con.getResources().getIdentifier("drawable/" + name , null, con.getPackageName());
}
}
When I run it, I get exception:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on a null object reference
I thought the problem was Context so I added con variable which gets context when code runs. I made some researches and tried another methods but I got same exception every time. Can anyone help me?
EDIT: When I use the same code line in MainActivity, it works perfectly. But in another class, it fails.
public class SomeMovie extends MovieActivity { }
public class MainActivity extends ActionBarActivity {
protected void onCreate(Bundle savedInstanceState) {
SomeMovie movie = new SomeMovie();
movie.updateScreen();
}
}
SomeMovie class is child of MovieActivity class. I call the method on that. I debugged the code and noticed that Context is null when code gets into updateScreen() method. Is it wrong to use inheritance on activity classes?
Change
int imageResource = con.getResources().getIdentifier("drawable/" + name , null, con.getPackageName());
to
int imageResource = getResources().getIdentifier("drawable/" + name , null, con.getPackageName());
You are already in an Activity's context, so use it. (No need to use the this keyword to refer to it)
EDIT:
You are not defining a layout for the SomeMovie Activity, so its context is always null.
You have to define the activity's layout in its onCreate method :
public class SomeMovie extends MovieActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.somemoviexml);
...
}
}
I have made a simple instrumented test to verify that if the data read from the SharedPreferences is displayed properly on the UI.Both data-retrieving and displaying actions are performed in Activity's onResume()method.
But the problem is,even if I've mocked the preference object and defined the fake return value,the activity still read data from the real preference,ignoring when(...).thenReturn(...)statement.Does anyone have any idea?
#RunWith(AndroidJUnit4.class)
public class EditProfileActivityTest {
#Mock
private UserPreference userPreference;
private String FAKE_NAME = "Test";
#Rule
public ActivityTestRule<EditProfileActivity> activityTestRule = new ActivityTestRule(EditProfileActivity.class,true,false);
#Before
public void setUp(){
//Set fake SharedPreferences
MockitoAnnotations.initMocks(this);
when(userPreference.getName()).thenReturn(FAKE_NAME);
//Start Activity
Intent intent = new Intent();
activityTestRule.launchActivity(intent);
}
#Test
public void showUserData() throws Exception{
onView(withId(R.id.name_tv)).check(matches(withText(FAKE_NAME)));
}
}
where UserPreference is a custom class which simply wraps SharedPreference class and contains lots of getters and setters.This is its constructor
public UserPreference(Context context) {
this.context = context;
sharedPreferences = this.context.getSharedPreferences("Pref", Context.MODE_PRIVATE);
prefEditor = sharedPreferences.edit();
}
and one of its getter and setter:
public String getName() {
return sharedPreferences.getString(context.getString(R.string.pref_name), "Guest");
}
public void saveName(String name){
prefEditor.putString(context.getString(R.string.pref_name), name);
prefEditor.apply();
}
[EDIT]
Simplified version of my original Activity:
public class EditProfileActivity extends AppCompatActivity{
//...
private UserPreference userPreference;
//...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
userPreference = new UserPreference(this);
setContentView(R.layout.activity_edit_profile);
//...
}
#Override
protected void onResume() {
super.onResume();
//...
String name = userPreference.getName();
nameEdt.setText(name); //Display the name on an EditText
//...
}
}
The UserPreference mock is created, but the activity still uses the one created in its onCreate method. You need to set that activity's userPreference field to your mock.
There are a few ways to do that:
Add a setter method for the userPreference field and call it in your #Before method:
#Before
public void setUp(){
...
EditProfileActivity activity = activityTestRule.launchActivity(intent);
activity.setUserPreference(mockedUserPreference);
}
This is simple but ugly: you alter the activity solely to accomodate the test.
Set the userPreference field via reflection:
#Before
public void setUp(){
...
EditProfileActivity activity = activityTestRule.launchActivity(intent);
Field userPreferenceField = activity.getClass().getDeclaredField("userPreference");
field.setAccessible(true);
userPreferenceField.set(activity, mockedUserPreference);
}
This is a brittle test: changing the field name breaks it without compile error. The activity doesnt have to be altered, though, so it is useful when you cant change it.
Don't instantiate the UserPreference in the onCreate method. In plain Java i'd add it as a constructor argument, but i don't know if that works as easily with Android. Maybe use a dependency injection framework, they're perfect to use with mocking: Android and Dependency Injection
I'm trying to push my tests in inter activity communications and check that for example after a correct login, I spawn the right activity (from 2 possibles activities).
Here's what my code looks like :
#RunWith(GuiceRobolectricJUnitRunner.class)
public class LoginActivityTest {
#Inject
private LoginActivity activity;
#Inject
private ExplorerActivity startedActivity ;
#Inject
private Context context;
private Button loginButton;
private EditText login;
private EditText password;
#Before
public void setUp() throws Exception {
activity.onCreate(null);
loginButton = (Button) activity.findViewById(R.id.identification_login_button);
login = (EditText) activity.findViewById(R.id.txtLogin);
password = (EditText) activity.findViewById(R.id.txtPassword);
}
#Online
#Test
public void shouldExploreWhenLoginIsCorrect() throws Exception {
assertNotNull(activity);
login.setText("test#test.com");
password.setText("test");
activity.setIntent(new Intent());
loginButton.performClick();
ShadowActivity shadowActivity = Robolectric.shadowOf(activity);
Intent startedIntent = shadowActivity.getNextStartedActivity();
ShadowIntent shadowIntent = Robolectric.shadowOf(startedIntent);
assertEquals(shadowIntent.getIntentClass(), ExplorerActivity.class);
// startedActivity.setIntent(startedIntent);
// startedActivity.onCreate(null);
}
}
My problem is that I can't retrieve the started activity from the shadowintent. Is there a way I could achieve something like that? Also, I don't see anytrace of my exploreractivity and I was wondering if Robolectric was doing work to sandbox every spawning process. I would really love an example of chained activity tests in robolectric. Thanks.
Since it was 3 months ago, you may have already found your answer, if not, you can use newInstance() on what you already have, then continue onwards as per normal.
ExplorerActivity explorerActivity = (ExplorerActivity) shadowIntent.getIntentClass().newInstance();
explorerActivity.setIntent(startedIntent);
explorerActivity.onCreate(null);