I'm trying to figure out how to handle errors with Room. I have the following interactor that inserts a task in the database:
TaskInteractor.java
public class TaskInteractor extends AbstractInteractor implements TaskContract.Interactor {
final TaskRepository mRepository;
interface Callback {
void onSuccess();
void onFailure(Throwable t);
}
#Inject
public TaskInteractor(WorkerThread workerThread,
MainThread mainThread,
TaskRepository repository) {
super(workerThread, mainThread);
this.mRepository = repository;
}
#Override
public void insertTask(final Task task, final Callback callback)
throws SQLiteException {
mWorkerThread.get().execute(new Runnable() {
#Override
public void run() {
try {
mRepository.insertTask(task);
} catch (SQLiteException exeption) {
Timber.e("Insertion failed. Exception: " + exeption.getMessage());
callback.onFailure(exeption);
throw exeption;
}
Timber.d("Insertion succeeded.");
callback.onSuccess();
}
});
}
}
In insertTask I use a try-catch block to check if a SQLiteException happened. If it does, I throw the exception.
But is this a good way of handling errors or is there maybe a better way?
Related
I'm try to add a simple RxJava call into a runnable thread so I can update the UI once the thread is completed. How do I go about doing that? Here is my Activity code:
public class PrintActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_printer);
printZpl("^XA^LL360^POI^FO20,20^A0N,25,25^FDThis is a test of the ZPL file printing on " + Helper.getCurrDateTime() + "^FS^XZ");
}
}
Here is the class which performs the runnable thread:
public class PrinterManager {
private static void printZpl(final String body, final String footer) {
new Thread(new Runnable() {
public void run() {
try {
Connection btCon = new BluetoothConnectionInsecure("AC:3F:A4:0E:22:05");
btCon.open();
btCon.write(body.getBytes());
Thread.sleep(500);
btCon.close();
// Insert RxJava return here to update the UI in the activity once the thread is completed.
} catch (Exception e) {
Timber.e(e.getMessage());
}
}
}).start();
}
}
I simplified the code for this posting. The actual code is much, much more complex...
Using RxJava2:
Completable.fromAction(() -> {
Connection btCon = new BluetoothConnectionInsecure("AC:3F:A4:0E:22:05");
btCon.open();
btCon.write(body.getBytes());
Thread.sleep(500);
btCon.close();
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
Use a Completable instead of an Observable since you do not emit anything but the completion event.
Wrap the asynchronous part of your code in an Observable like this:
public class PrinterManager {
public static Observable<Void> printZpl(final String body, final String footer) {
return Observable.fromCallable(new Callable<Void>() {
#Override
public Void call() throws Exception {
try {
Connection btCon = new BluetoothConnectionInsecure("AC:3F:A4:0E:22:05");
btCon.open();
btCon.write(body.getBytes());
Thread.sleep(500);
btCon.close();
} catch (Exception e) {
Timber.e(e.getMessage());
}
return null;
}
});
}
}
Then, in your Activity, subscribe to it, triggering the code inside:
public class PrintActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_printer);
PrinterManager.printZpl("^XA^LL360^POI^FO20,20^A0N,25,25^FDThis is a test of the ZPL file printing on " + Helper.getCurrDateTime() + "^FS^XZ")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
}
}
If you haven't already, you will need to add the dependencies to your app.gradle file:
compile "io.reactivex:rxandroid:1.2.0"
compile "io.reactivex:rxjava:1.2.0"
If you want to update the UI, then pass an Observer to the subscribe method, instead of just using the empty one as in my example above.
The code below won't crash when running in JUnit environment. But it crashes when running in the app. I can see error logs in the console, but tests are marked as passed.
#Test
public void test() {
Observable observable = Observable.error(new RuntimeException());
observable.subscribe();
}
So, the question is: how to make it crash in JUnit. Because yeah, if something doesn't work in the app it's a good thing if it doesn't work in the unit tests also :)
And in this example I have direct access to the observable. But in my real tests I don't have that. Real observables are just internal details of classes that being tested. The most thing I can to do is to inject schedulers or something.
So, how to make it crash without having direct access to the observable?
Also, I've just checked this code doesn't crash either:
#Test
public void test() {
Observable observable = Observable.error(new RuntimeException());
observable.subscribe(new Consumer() {
#Override
public void accept(Object o) throws Exception {
throw new RuntimeException();
}
}, new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
throw new RuntimeException();
}
});
}
According to akarnokd this is RxJava2 specific problem.
"Such usages no longer throw synchronously in 2.x but end up in the plugin handler for errors."
It is possible to check if any errors was thrown with this code
public static List<Throwable> trackPluginErrors() {
final List<Throwable> list = Collections.synchronizedList(new ArrayList<Throwable>());
RxJavaPlugins.setErrorHandler(new Consumer<Throwable>() {
#Override
public void accept(Throwable t) {
list.add(t);
}
});
return list;
}
A small trick I use to tackle this problem, is creating a JUnit4 TestRule class that setups a custom RxJava error handler so it can throw when an unhandled RxJava error occurs:
/**
* A rule that detects RxJava silent errors and reports them to JUnit
(by throwing them).
*/
public class RxErrorRule implements TestRule {
#Override
public Statement apply(Statement base, Description description) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
Consumer<? super Throwable> previous = RxJavaPlugins.getErrorHandler();
AtomicReference<Throwable> result = setupErrorHandler();
try {
base.evaluate();
} finally {
RxJavaPlugins.setErrorHandler(previous);
}
if (result.get() != null) {
throw result.get();
}
}
};
}
private AtomicReference<Throwable> setupErrorHandler() {
AtomicReference<Throwable> result = new AtomicReference<>();
RxJavaPlugins.setErrorHandler(new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) {
result.set(throwable);
}
});
return result;
}
}
And in the unit test:
public class YourRxTest {
#Rule
public RxErrorRule errorRule = new RxErrorRule();
// ...
}
Use TestSubscriber
Observable observable = Observable.error(new RuntimeException());
TestSubscriber testSubscriber = TestSubscriber.create();
observable.subscribe(testSubscriber);
testSubscriber.assertTerminalEvent();
testSubscriber.assertError(RuntimeException.class);
I am trying to learn about rxJava and reactive programming in context of android and I feel I am nearly there, I just can't quite grasp the complete picture to fully understand what I am doing.
I have the below code which gets a list of instances of a class called iApps from the database
myHelper m = new myHelper(getApplication());
m.getApps()
.observeOn(Schedulers.newThread())
.subscribe(currentApps::addAll,
throwable -> Log.e("Error Observable", throwable.toString() + " " + Arrays.toString(throwable.getStackTrace())),
() -> compareLists(availableApps, currentApps));
}
Which uses the following methods:
//From my database caller function
public Callable<ArrayList<iApp>> getApps()
{
return this::getCurrentInfo;
}
A custom helper function
public class myHelper {
Context ctx;
tQuery t;
public myHelper(Context _ctx)
{
this.ctx = _ctx;
t = new tQuery(_ctx);
}
Observable<ArrayList<iApp>> getApps()
{
return makeObservable(t.getApps())
.subscribeOn(Schedulers.computation());
}
private static <T> Observable<T> makeObservable(final Callable<T> func) {
return Observable.create(
new Observable.OnSubscribe<T>() {
#Override
public void call(Subscriber<? super T> subscriber) {
try {
subscriber.onNext(func.call());
} catch (Exception ex) {
subscriber.onError(ex);
}
}
});
}
}
However my on complete never runs. I have checked the onNext by looping through the results of iApp and outputting one of the fields so I can see that the data is being collected, however my compareLists function is never run.
Could someone explain my oversight?
Well that was embarassing!
private static <T> Observable<T> makeObservable(final Callable<T> func) {
return Observable.create(
new Observable.OnSubscribe<T>() {
#Override
public void call(Subscriber<? super T> subscriber) {
try {
subscriber.onNext(func.call());
subscriber.onCompleted();
} catch (Exception ex) {
subscriber.onError(ex);
}
}
});
}
In my Android application I have set up Volley.
Robolectric.application is initialized and all other tests runs smoothly.
I get this error when trying to get mocked HTTP response.
This is my test:
#RunWith(MyRobolectricTestRunner.class)
public class ApiTests {
#Inject
protected Api api;
#Before
public void setUp() {
ObjectGraph.create(new AndroidModule(Robolectric.application), new TestApplicationModule()).inject(this);
}
#Test
public void shouldGetErrorList() throws Exception {
Project project = new Project("test", "test", "test", DateTime.now());
addPendingProjectsErrorsResponse("response.json"); //adding response to FakeHttpLayer
api.getProjectErrors(project, new Listener<ProjectErrors>() {
#Override
public void onResponse(ProjectErrors response) {
assertNotNull(response);
}
}, new ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
throw new RuntimeException(error);
}
}
);
}
}
This is error I get:
Exception in thread "Thread-3" java.lang.NullPointerException
at org.robolectric.shadows.ShadowLooper.getMainLooper(ShadowLooper.java:59)
at android.os.Looper.getMainLooper(Looper.java)
at org.robolectric.Robolectric.getUiThreadScheduler(Robolectric.java:1301)
at org.robolectric.shadows.ShadowSystemClock.now(ShadowSystemClock.java:15)
at org.robolectric.shadows.ShadowSystemClock.uptimeMillis(ShadowSystemClock.java:25)
at org.robolectric.shadows.ShadowSystemClock.elapsedRealtime(ShadowSystemClock.java:30)
at android.os.SystemClock.elapsedRealtime(SystemClock.java)
at com.android.volley.VolleyLog$MarkerLog.add(VolleyLog.java:114)
at com.android.volley.Request.addMarker(Request.java:174)
at com.android.volley.CacheDispatcher.run(CacheDispatcher.java:92)
I had same error and avoid it by using my own (and ugly) SystemClock shadow.
shadow class:
#Implements(value = SystemClock.class, callThroughByDefault = true)
public static class MyShadowSystemClock {
public static long elapsedRealtime() {
return 0;
}
}
test code:
#Test
#Config(shadows = { MyShadowSystemClock.class, ... })
public void myTest() {
}
Another workaround would be to disable Volley logging by calling
VolleyLog.DEBUG = false;
in your setUp method.
is there a more elegant way to do an assert throws exception in Android then this?
public void testGetNonExistingKey() {
try {
alarm.getValue("NotExistingValue");
fail( );
} catch (ElementNotFoundException e) {
}
}
Something like this does not work?!
#Test(expected=ElementNotFoundException .class)
Thanks, Mark
Are you using a junit4 test runner? The #Test annotation won't work if you're running a junit3 test runner. Check the version that you're using.
Secondly, the recommended way to check for exceptions in your code is to use a Rule (introduced in junit 4.7).
#Rule
public ExpectedException exception = ExpectedException.none();
#Test
public void throwsIllegalArgumentExceptionIfIconIsNull() {
// do something
exception.expect(IllegalArgumentException.class);
exception.expectMessage("Icon is null, not a file, or doesn't exist.");
new DigitalAssetManager(null, null);
}
You can continue to use the #Test(expected=IOException.class), but the above has the advantage that if an exception is thrown before the exception.expect is called, then the test will fail.
I did something very similar to hopia's answer with a couple of improvements. I made it return the exception object so that you can check its message or any other properties, and I declared a Testable interface to replace Runnable because Runnable doesn't let your code under test throw checked exceptions.
public interface Testable {
public void run() throws Exception;
}
public <T extends Exception> T assertThrows(
final Class<T> expected,
final Testable codeUnderTest) throws Exception {
T result = null;
try {
codeUnderTest.run();
fail("Expecting exception but none was thrown.");
} catch(final Exception actual) {
if (expected.isInstance(actual)) {
result = expected.cast(actual);
}
else {
throw actual;
}
}
return result;
}
Here's an example of calling it.
InvalidWordException ex = assertThrows(
InvalidWordException.class,
new Testable() {
#Override
public void run() throws Exception {
model.makeWord("FORG", player2);
}
});
assertEquals(
"message",
"FORG is not in the dictionary.",
ex.getMessage());
If you're using Kotlin, you can take advantage of reified types to avoid passing the Exception subclass as an argument:
inline fun <reified T : Exception> assertThrows(runnable: () -> Any?) {
try {
runnable.invoke()
} catch (e: Throwable) {
if (e is T) {
return
}
Assert.fail("expected ${T::class.qualifiedName} but caught " +
"${e::class.qualifiedName} instead")
}
Assert.fail("expected ${T::class.qualifiedName}")
}
#Test
fun exampleTest() {
val a = arrayOf(1, 2, 3)
assertThrows<IndexOutOfBoundsException> {
a[5]
}
}
This is how I do it. I create a static method called assertThrowsException that takes in as arguments an expected exception class and a Runnable which contains the code under test.
import junit.framework.Assert;
public SpecialAsserts {
public void assertThrowsException(final Class<? extends Exception> expected, final Runnable codeUnderTest) {
try {
codeUnderTest.run();
Assert.fail("Expecting exception but none was thrown.");
} catch(final Throwable result) {
if (!expected.isInstance(result)) {
Assert.fail("Exception was thrown was unexpected.");
}
}
}
}
This is the sample code to use the special assert in your test class (that extends AndroidTestCase or one of its derivatives):
public void testShouldThrowInvalidParameterException() {
SpecialAsserts.assertThrowsException(InvalidParameterException.class, new Runnable() {
public void run() {
callFuncThatShouldThrow();
}
});
}
Yes, there's a lot of work, but it's better than porting junit4 to android.
With junit3 the following might help.
public static void assertThrows(Class<? extends Throwable> expected,
Runnable runnable) {
try {
runnable.run();
} catch (Throwable t) {
if (!expected.isInstance(t)) {
Assert.fail("Unexpected Throwable thrown.");
}
return;
}
Assert.fail("Expecting thrown Throwable but none thrown.");
}
public static void assertNoThrow(Runnable runnable) {
try {
runnable.run();
} catch (Throwable t) {
Assert.fail("Throwable was unexpectedly thrown.");
}
}