Android - Testing a simple Activity method - android

i'm writing you today because i have a real and concrete question about how testing an activity method. Let me explain the situation.
I have an activity (A) which use a method save(param) from a service (S). This method throws a specific exception if the parameter is invalid.
Unit tests are written for this part, and now, i would like to test the Activity part :
In my Activty, i use the following code to call the save() method
public void OnSaveClicked()
{
try{
if ( S.save(my_object) > 0 ) // Object Saved
{
ShowToast(this, "Your object has been saved successfully !");
}
else { // Error occured with the database
ShowToast(this, "An error occured with the database");
}
catch (MyException ex)
{
ShowToast(this, "The object you are trying to save is not valid... Please check information you entered");
}
}
The test that i'm trying to write has to check if the save() method raised the exception or not. To do that, i simulate a click on the UI Button, but, i don't really know what do i have to check. I read that's not possible to write an assert on a toast message. I'm currently thinking about using a dialog but i am not sure that the way i'm thinking is the right one. Is it normal to test that or not ?
Would you give me your feedback about that, and discuss about the right way to test activity ?
Thanks,
t00f

You might want to look at Robolectric. They have support for assertions like this http://groups.google.com/group/robolectric/browse_thread/thread/87952e620ce1bb37?pli=1
Thanks,
Ian

Related

Android alertdialog and exceptions

I have a fragment that asks for some input and then has a submit button. When the submit button on that page is hit it goes to a viewmodel routine.
submitButton.setOnClickListener {
viewModel.submitted(binding)
}
The viewModel.submitted routine checks the sanity of the values entered that are available in the binding value. If the sanity checks fail I want to show an AlertDialog. If everything is sane then viewModel.submitted adds a record to the database. I would like to show an AlertDialog to show a new record was created successfully.
I am following the instructions here https://developer.android.com/guide/topics/ui/dialogs. It says to create a DialogFragment that defines a AlertDialog. I modified the onClickListener lamda so that if there is a sanity check error, ViewModel.submitted throws an exception which is caught and shows an alertDialog.
submitButton.setOnClickListener {
try {
viewModel.submitted(binding)
} catch (e: Exception) {
val alert = AddSymbolDialogFragment()
alert.message = e.message.toString()
alert.show(parentFragmentManager, "Test1")
}
}
This worked for the checks in the viewModel.submitted() routine that were just checking the values in the binding that is passed to it. But in viewModel.submitted() I need to check some of the binding values against values in the database. So, inside viewModel.submitted() I launch a coroutine to get the database info to compare against the binding values. If I throw an exception inside the coroutine the app dies. This makes sense because the setOnClickListener() lambda function has already completed. So, there is nothing to catch it.
I have worked around this by passing the parentFormatManager to viewModel.submitted:
submitButton.setOnClickListener {
viewModel.submitted(binding, parentFragmentManager)
}
Note: the instruction page mentioned above in the "Showing a Dialog" section it says to use getFragmentManager from Fragment, but Android Studio says that is deprecated and to use parentFormatManager.
Then inside viewModel.submitted() I show the alert when I get an error either before the coroutine or after the coroutine:
val alert = AddSymbolDialogFragment()
alert.message = "bad Category"
alert.show(fragmentManager, "Test1")
That seems to work.
It is the viewModel.submitted() routine's job to update the data with the info in the binding parameter. When it is successful I will also want to show an AlertDialog just to show it was successful.
So, I have a setup that works. But, I am unsure it is the correct way to do it. It just doesn't feel right to have the viewModel show an alertDialog. Seems that ought to be done at the ui level.
Is there a more appropriate way to show alertDialogs rather than passing the Fragment's parentFragmentManager to the viewModel routine that performs the I/O work?
There is also kind of a bigger question here. When I launch a coroutine and an exception occurs in the coroutine how in general do you pass that exception up to higher levels of the code.
Thanks

Android - Crashlytics, run code during crash

I had a bad crash case that was caused due to some Asyncs doing stuff in improper order in a SQLite and thing blew up. It took me some time to debug all that and access to the internal db would have helped immensely. I know how to access that internal db on a dev device but in case something goes wrong I would like to be able to get an instance of that db no matter the device. For error reporting I am using Crashlytics.
The question is: Is there a way to have Crashlytics run a piece of code (method, etc) during the crash collection/reporting? (For example, get db copy and email it, or something)
Couldn't find something in the documentation.
It is possible to get control prior to Crashlytics logging a crash. You essentially have to create your own uncaught exception handler and call Crashlytics' handler from there. Something like this in your Application class:
private UncaughtExceptionHandler originalUncaughtHandler;
#Override
public void onCreate() {
// initialize Fabric with Crashlytics
originalUncaughtHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
// do the rest of your oncreate stuff
}
#Override
public void uncaughtException(Thread thread, Throwable ex) {
// do your work to add data to Crashlytics log
originalUncaughtHandler.uncaughtException(thread, ex);
}
No you can't. You can however set certain values before initiating Crashlytics. Like adding values to parameters so as to identify user. Like adding email id of user before creating a crashlytics session.
As #basu-singh said, you can add context to the crash, see https://docs.fabric.io/android/crashlytics/enhanced-reports.html
Or you can use your own UncaughtExceptionHandler, and then call Crashlytics. Though your code needs to be extra safe !

How to unit test Log.e in android?

I need to perform an unit test where I need to check if an error message is logged when a certain condition occurs in my app.
try {
//do something
} catch (ClassCastException | IndexOutOfBoundsException e) {
Log.e(INFOTAG, "Exception "+e.getMessage());
}
How can I test this? I am getting the below error while unit testing.
Caused by: java.lang.RuntimeException: Method e in android.util.Log not mocked.
There are two ways to do this:
You turn to Powermock or Powermokito; as those mocking frameworks will allow you to mock/check that static call to Log.e().
You could consider replacing the static call.
Example:
interface LogWrapper {
public void e( whatever Log.e needs);
}
class LogImpl implements LogWrapper {
#Override
e ( whatever ) {
Log.e (whatever) ;
}
And then, you have to use dependency injection to make a LogWrapper object available within the classes you want to log. For normal "production" usage, that object is simply an instance of LogImpl; for testing, you can either use a self-written impl (that keeps track of the logs send to it); or you can use any of the non-power mocking frameworks (like EasyMock or Mokito) to mock it. And then you use the checking/verification aspect of the mocking framework to check "log was called with the expected parametes".
Please note: depending on your setup, option 2 might be overkill. But me, personally, I avoid any usage of Powermock; simply because I have wasted too many hours of my life hunting down bizarre problems with Powermock. And I like to do coverage measurements; and sometimes Powermock gives you problems there, too.
But as you are asking about Powermock, you basically want to look here (powermockito) or here (powermock). And for the record: try using your favorite search engine the next time. It is really not like you are the first person asking this.

Rising error in Robolectric test when you have try-catch in your application code

In my android application I have written try-catch in every event method. So when an exception occurs, the catch gets the exception and a method shows a message box containing the exception details and I can handle and find my application's bugs.
For example:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
}
catch (Exception e) {
MessageBox.showException(this, e);
}
}
Now in Robolectric which there is no device to show the ui results, I cannot find out if an exception occurred. Now I want to do something when my code went to catch part or when MessageBox.showException is called, the test fails.
How can I do that?
The only way I can think of solving this is for you to inject the component that handles the errors into the classes that use it and after that, load a customized one for your tests.
There are several ways to achieve this and probably some better than what I will suggest, but I will try and present the option that requires minimum changes to what I think is your current architecture.
1 - Whatever you use for showing exceptions, instantiate this in your Application class and keep it there. Or at least provide it from your application class, so now whenever you need to use MessageBox, instead of a static method, you fetch it from the Application first. For example:
((MyApplication)getApplication()).getMessageBox().showException(this,e)
2 - Create a TestMessageBox and a TestApplication (that extends your normal Application class). In your TestApplication, override getMessageBox() to return the TestMessageBox instead of the normal MessageBox. In your TestMessageBox do whatever you want to be able to observe the errors in your tests.
3 - In your tests, use the TestApplication. When you run tests, Robolectric will load this instead of the normal application so your tests will now use your TestMessageBox and you can capture the info you need.
#Config(application = TestApplication.class)

perform a task when android automation pass or fail (robotium)

I am trying to perform a task when android is fail or pass, is there a way to do this, and where should I put my code?
was thinking to do it in tearDown, but is there a way to check whether the testcase is pass or not??
When your test fails - that is an exception which is thrown.
I would suggest you to put the code related to your test case in try/catch block and in catch block do the stuff you want to do on test failure.
In order to mark that test case as failure you can throw the exception at the end of catch block.
e.g:
try
{
solo.....
//do your stuff
}
catch(Throwable e)
{
//Do what you want to do on test failure.
throw e;
}
You will need to register a test listener to get the pass or fail stance automatically, this however is a little bit of a pain, you will either need to override your instrumentation test runner, or use reflection to get the AndroidTestRunner from your current instrumentation test runner and then you can add in the listener as seen here http://developer.android.com/reference/android/test/AndroidTestRunner.html
There are other ways too but they are all a little bit of manual work (such as catching assertion exceptions within test methods and doing failure case that way.
looks like I've found the solution.
we can override the runTest() method, and do this:
super.runTest():
doSomething()
doSomething() will be executed when test passed..

Categories

Resources