touchlab / Kermit

Kermit by Touchlab is a Kotlin Multiplatform centralized logging utility.
https://kermit.touchlab.co
Apache License 2.0
724 stars 41 forks source link

New version missing a test writter #184

Open mustafaozhan opened 3 years ago

mustafaozhan commented 3 years ago

Currently, there is no LogWriter to use in tests In old Kermit I was using the CommonLogger() for test without problem, but the new Kermit (Logger) doesn't have it.

It causes this issue:

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

Method println in android.util.Log not mocked. See http://g.co/androidstudio/not-mocked for details.
java.lang.RuntimeException: Method println in android.util.Log not mocked. See http://g.co/androidstudio/not-mocked for details.
    at android.util.Log.println(Log.java)
    at co.touchlab.kermit.LogcatWriter.log(LogcatWriter.kt:30)
    at co.touchlab.kermit.Logger.log(Logger.kt:155)
    at com.github.mustafaozhan.ccc.common.api.ApiRepositoryImpl.getRatesViaBackend(ApiRepositoryImpl.kt:46)
    at com.github.mustafaozhan.ccc.common.api.ApiRepositoryImpl$getRatesViaBackend$1.invokeSuspend(ApiRepositoryImpl.kt)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:277)
    at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:87)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:61)
    at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:40)
    at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
    at com.github.mustafaozhan.ccc.common.ActualKt.runTest(Actual.kt:13)
    at com.github.mustafaozhan.ccc.common.repo.ApiRepositoryTest.getRatesViaBackendParameterCanNotBeEmpty(ApiRepositoryTest.kt:29)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
    at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
    at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
    at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
    at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
    at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:133)
    at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
    at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
    at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)

Maybe we need the getTestLogWriter() publicly avaliable so tests will not fail due to Method println in android.util.Log not mocked.

ie:

fun initLogger(
    forTest: Boolean = false
) = Logger.setLogWriters(
    if (forTest) {
        getTestLogWriter()
    } else {
        CustomWritter()
    }
)
mustafaozhan commented 3 years ago

Hmm, not sure getTestLogWriter is actually doing, is it logging something when you run the test ?

I end up writing my custom Writer

fun initLogger(
    forTest: Boolean = false
) = Logger.setLogWriters(
    if (forTest) {
        TestWriter()
    } else {
        CustomWritter()
    }
)
class TestWriter : LogWriter() {
    override fun log(
        severity: Severity,
        message: String,
        tag: String,
        throwable: Throwable?
    ) {
        println("${severity.name}: $tag $message ${throwable ?: ""}")
    }
}
kpgalligan commented 3 years ago

CommonWriter is available. It looks like the test code is using LogcatWriter in test code, which I'd guess is calling into something that robolectric or similar is providing?

Out of the box, the new version of Kermit uses the convenience function platformLogWriter(), which will create and instance of LogcatWriter on Android.

In test setup you should be able to just use CommonWriter. We could maybe wrap calls in LogcatWriter in try/catch and assume we're in tests is they throw, although I want to think about that a bit.

I also added TestLogWriter as an alternative. I haven't thought about that at all, but was doing a whole bunch of stuff today, so threw it in there as a start.

getTestLogWriter() is internal to Kermit's tests, so you can't call that directly.

To try things out, I'm pushing 1.0.0-SNAPSHOT now, and I would expect actual 1.0.0 soon (next few days).

kpgalligan commented 3 years ago

Hmm. Not closing this yet. Need to dig in more.

kpgalligan commented 2 years ago

TestWriter doesn't print anything, CommonWriter doesn't let you assert anything. We can either document how to init tests to do what you want, or we'd need to discuss code changes for default.

psh commented 2 years ago

Repo that reproduces the issue - https://github.com/touchlab/kermit-missing-log-writer - specifically https://github.com/touchlab/kermit-missing-log-writer/blob/main/androidApp/src/test/java/co/touchlab/kermit/kermitmissinglogwriter/android/SomeFullyAndroidRepositoryTest.kt