3

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

Roc Boronat
  • 11,395
  • 5
  • 47
  • 59
Thibaut Mottet
  • 414
  • 4
  • 15

3 Answers3

2

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.)

Thibaut Mottet
  • 414
  • 4
  • 15
1

Use flavors.

By default, you have flavor called main, where you should put all of base your code, assets and manifests, that are used in development, testing and production. You can easily create a new flavor by adding your flavor name under buildTypes { ... } in your module's build.gradle file. This lets you split dependencies and easily remove anything that you do not want or even need in your release builds. You can read more about flavors here. This way, you'll be able to separate core code in main flavor and add crashlytics in your release builds only.

For example, in your main flavor:

open class BaseApplication : Application()
{ 
    fun onCreate()
    {
       [common code between flavors]
    }
}

Then in your release flavor

open class MainApplication : BaseApplication()
{
    fun onCreate()
    {
      [initialize crashlytics]
    }
}

And in your other flavors

open class MainApplication : BaseApplication()

Note: Use MainApplication instead of BaseApplication in your manifest.

Dragas
  • 1,140
  • 13
  • 29
0

Instead of disabling it on every Espresso test, we avoid enabling it when running the debug build type.

So, in your Application class, build Crashlytics and Answers like this:

CrashlyticsCore crashlyticsCore = new CrashlyticsCore.Builder()
    .disabled(BuildConfig.DEBUG)
    .build();

Fabric.with(this,
    new Crashlytics.Builder().core(crashlyticsCore).build(),
    new Answers());
}
Roc Boronat
  • 11,395
  • 5
  • 47
  • 59