The previous Fabric SDK had a method Fabric.isInitialized() that allowed you to check if Fabric was initialized before doing something like logging an exception. This allowed you to put a guard around methods in unit tests.
I would use it to put a guard around things that unit tests might touch when Firebase is not initialized. Example:
fun logException(ex: Throwable, nodeInfoCompat: AccessibilityNodeInfoCompat?) {
// We check to make sure Fabric is initialized or else the tests will fail - see above comment
if (Fabric.isInitialized()) {
crashSetParam(CrashParameters.ROOT_NODE, nodeInfoCompat.toString())
Crashlytics.logException(ex)
}
}
I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.
A possible solution that I've found is putting this in the main application class
if (FirebaseApp.getApps(getApplicationContext()).isEmpty()) {
FirebaseApp.initializeApp(getApplicationContext());
}
I've only tested it for a bit but while running tests it would always return true for isEmpty() and on normal utilization it would return false.
I had the same error(ExceptionInInitializerError, NoClassDefFoundError) using FirebaseCrashlytics.getInstance() in unit test.
You can also wrap the error by try-catch-when like this:
try {
FirebaseCrashlytics.getInstance().recordException(e)
} catch (e: Error) {
when (e) {
is ExceptionInInitializerError, is NoClassDefFoundError -> {}
else -> throw e
}
}
It works in my case.
Thanks for the request. Your observations are correct. There's no equivalent API that Firebase has. I do think think you could solve this with dependency injection though, which may lead to better health and testability for your codebase overall.
This is particularly true since the entry points to Firebase APIs are singletons, that can be hard to test without clean dependency injection.
Let us know how that works
public class Foo {
public void bar(FirebaseCrashlytics f) {
FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
crashlytics.log("");
}
}
can be changed to
public class Foo {
private final FirebaseCrashlytics crashlytics;
public Foo(FirebaseCrashlytics f) {
this.crashlytics = f;
}
public void bar() {
crashlytics.log("");
}
}
Hey @darran-kelinske-fivestars. We need more information to resolve this issue but there hasn't been an update in 5 weekdays. I'm marking the issue as stale and if there are no new updates in the next 5 days I will close it automatically.
If you have more information that will help us get to the bottom of this, just add a comment!
The issue was waiting for action from your team.
What additional information do you need?
@darran-kelinske-fivestars We were hoping to see if the comment above helped you work around the problem https://github.com/firebase/firebase-android-sdk/issues/1437#issuecomment-638533749
@ashwinraghav I don't want to initialize Firebase Crashlytics for Unit test. What your can advice for this situation?
Does the pattern above not work for you?
Yes, not work for me. We have legacy codebase with static methods for reporting to crashlytics
I can leave the issue open as a feature request. But, there's not another workaround that does not involve any refactoring.
thank you for keeping it open as a feature request - it is also a breaking change in the API too
any news on this?
@ashwinraghav any update on this. Unit test fails with FirebaseCrashlytics.getInstance().log(logMessage) cause of NULL POINTER EXCEPTION
@KalaiSelvanG can you share the stack trace? Additionally: what is the gradle version and android gradle plugin version that you're using?
@KalaiSelvanG can you share the stack trace? Additionally: what is the gradle version and android gradle plugin version that you're using?
@vkryachko
gradle-5.6.4
plugin version - 3.4.0
java.lang.AssertionError: Exceptions differ; expected: class com.abc.ABCInitFailedException, actual:
java.lang.NullPointerException
at rx.observers.TestSubscriber.assertError(TestSubscriber.java:543)
at com.abc.manager.ABCManagerTest.testFactorManager(ABCManagerTest.java:74)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:310)
at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:89)
at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:97)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:294)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:127)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:82)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:282)
at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:87)
at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:50)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:207)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:146)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:120)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:122)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:106)
at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:59)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.NullPointerException
at com.abc.tools.CrashlyticsHelper.log(CrashlyticsHelper.java:28)
at com.abc.manager.ABCManagerImpl$2.call(ABCManagerImpl.java:114)
at com.abc.manager.ABCManagerImpl$2.call(ABCManagerImpl.java:101)
at rx.internal.util.ScalarSynchronousSingle$2.call(ScalarSynchronousSingle.java:128)
at rx.internal.util.ScalarSynchronousSingle$2.call(ScalarSynchronousSingle.java:124)
at rx.Single.unsafeSubscribe(Single.java:1693)
at rx.Single.subscribe(Single.java:1790)
at com.abc.manager.ABCManagerTest.testFactorManager(ABCManagerTest.java:73)
... 28 more
Process finished with exit code -1
CrashlyticsHelper.class
public static void log(String logMessage) {
FirebaseCrashlytics.getInstance().log(logMessage);
}
@vkryachko any update on this?