Search code examples
androidkotlinandroid-testinggoogle-fabric

How to disable Fabric when running tests


How can I disable Fabric: Crashlytics and Answer when running tests?

Is there any other way to disable Crashlytics during the test instead of putting this code before every test?

@LargeTest
@RunWith(AndroidJUnit4::class)
class AcceptanceTest {
    @Before
    fun setUp() {
          val crashlyticsKit = Crashlytics.Builder()
            .core(CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build())
            .build()
          Fabric.with(InstrumentationRegistry.getContext(), crashlyticsKit)
    }

}

and avoiding to put a global boolean like IS_TEST_MODE


Solution

  • I find a better and cleaner way to disable and manage Fabric.

    In my application I use slf4j-api, a log API used a lot in web development. With this API you can create appender, so I decided to create a Fabric Appender and use this appender only when the app run.

    How to

    Install slf4j with Logback

    Set your build.gradle

    dependencies {
        ...
        // Log
        compile 'com.github.tony19:logback-android-core:1.1.1-6'
        compile 'com.github.tony19:logback-android-classic:1.1.1-6'
        compile 'org.slf4j:slf4j-api:1.7.21'
    }
    

    Add src/main/assets/logback.xml

    <configuration>
        <appender name="FABRIC" class="path/to/your/FabricAppender" />
        <appender name="LOGCAT" class="ch.qos.logback.classic.android.LogcatAppender">
            <tagEncoder>
                <pattern>%logger{0}</pattern>
            </tagEncoder>
            <encoder>
                <pattern>[PUP] %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
            </encoder>
        </appender>
    
    <root level="INFO">
        <appender-ref ref="LOGCAT" />
        <appender-ref ref="FABRIC" />
    </root>
    

    By default Logback has a customize appender working with Logback. You can comment the FABRIC part we will be explain later.

    Add src/androidTest/assets/logback.xml

    <configuration>
        <appender name="LOGCAT" class="ch.qos.logback.classic.android.LogcatAppender">
            <tagEncoder>
                <pattern>%logger{0}</pattern>
            </tagEncoder>
            <encoder>
                <pattern>[PUP] %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
            </encoder>
        </appender>
    
        <root level="DEBUG">
            <appender-ref ref="LOGCAT" />
        </root>
    </configuration>
    

    You don't want to have Fabric appender here! because you want your instrumented test running only with Logcat

    Use Logback

    Now you can easily log using the powerful API of slf4j like below

    class MainActivity : AppCompatActivity() {
        private val log = LoggerFactory.getLogger(javaClass)!! // Call your logger in each class with this line
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            log.info("Activity start")
            // ...
        } }
    

    Set Fabric for Logback

    Create in your project FabricAppender.kt

    package your.package.log
    
    import android.content.Context
    import ch.qos.logback.classic.Level
    import ch.qos.logback.classic.spi.ILoggingEvent
    import ch.qos.logback.classic.spi.ThrowableProxy
    import ch.qos.logback.core.UnsynchronizedAppenderBase
    import com.crashlytics.android.Crashlytics
    import com.crashlytics.android.answers.Answers
    import com.crashlytics.android.answers.CustomEvent
    import io.fabric.sdk.android.Fabric
    
    class FabricAppender : UnsynchronizedAppenderBase<ILoggingEvent>() {
        companion object {
            private var isFabricInit = false
    
            fun init(context: Context) {
                Fabric.with(context, Crashlytics(), Answers())
                isFabricInit = true
            }
        }
    
        override fun append(event: ILoggingEvent) {
            if (isFabricInit.not()) return
            when (event.level.levelInt) {
                Level.ERROR_INT -> {
                    val throwable = (event.throwableProxy as ThrowableProxy).throwable
                    if (throwable != null) {
                        Crashlytics.logException(throwable)
                    } else {
                        Crashlytics.log(event.message.replace(": {}", ""))
                    }
                }
            }
        }
    }
    

    This appender will send exception to Crashlytics every time you call log.error("Something wrong happen!!"). As you can see, you can fully customized the appender.

    Init the appender when application start

    class MainActivity : AppCompatActivity() {
        private val log = LoggerFactory.getLogger(javaClass)!! // Call your logger in each class with this line
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            FabricAppender.init(this) // Init before the first log
    
            log.info("Activity start")
            // ...
        } }
    

    Finally

    Now your fabric will be totally isolated to your code by using the slf4j API, and will be not use during your instrumented test yataaa!!! You can find other appender on internet (send to ELK, file, etc.)