Here Utils.java is my class to be tested and following is the method which is called in UtilsTest class. Even if I am mocking Log.e method as shown below
@B
Thanks to @Paglian answer and @Miha_x64 comment, I was able to make the same thing work for kotlin.
Add the following Log.kt file in app/src/test/java/android/util
@file:JvmName("Log")
package android.util
fun e(tag: String, msg: String, t: Throwable): Int {
println("ERROR: $tag: $msg")
return 0
}
fun e(tag: String, msg: String): Int {
println("ERROR: $tag: $msg")
return 0
}
fun w(tag: String, msg: String): Int {
println("WARN: $tag: $msg")
return 0
}
// add other functions if required...
And voilà, your calls to Log.xxx should call theses functions instead.
Using PowerMock
one can mock Log.i/e/w static methods from Android logger. Of course ideally you should create a logging interface or a facade and provide a way of logging to different sources.
This is a complete solution in Kotlin:
import org.powermock.modules.junit4.PowerMockRunner
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
/**
* Logger Unit tests
*/
@RunWith(PowerMockRunner::class)
@PrepareForTest(Log::class)
class McLogTest {
@Before
fun beforeTest() {
PowerMockito.mockStatic(Log::class.java)
Mockito.`when`(Log.i(any(), any())).then {
println(it.arguments[1] as String)
1
}
}
@Test
fun logInfo() {
Log.i("TAG1,", "This is a samle info log content -> 123")
}
}
remember to add dependencies in gradle:
dependencies {
testImplementation "junit:junit:4.12"
testImplementation "org.mockito:mockito-core:2.15.0"
testImplementation "io.kotlintest:kotlintest:2.0.7"
testImplementation 'org.powermock:powermock-module-junit4-rule:2.0.0-beta.5'
testImplementation 'org.powermock:powermock-core:2.0.0-beta.5'
testImplementation 'org.powermock:powermock-module-junit4:2.0.0-beta.5'
testImplementation 'org.powermock:powermock-api-mockito2:2.0.0-beta.5'
}
To mock Log.println
method use:
Mockito.`when`(Log.println(anyInt(), any(), any())).then {
println(it.arguments[2] as String)
1
}
If using Kotlin I would recommend using a modern library like mockk which has built-in handling for statics and many other things. Then it can be done with this:
mockkStatic(Log::class)
every { Log.v(any(), any()) } returns 0
every { Log.d(any(), any()) } returns 0
every { Log.i(any(), any()) } returns 0
every { Log.e(any(), any()) } returns 0
Another solution is to use Robolectric. If you want to try it, check its setup.
In your module's build.gradle, add the following
testImplementation "org.robolectric:robolectric:3.8"
android {
testOptions {
unitTests {
includeAndroidResources = true
}
}
}
And in your test class,
@RunWith(RobolectricTestRunner.class)
public class SandwichTest {
@Before
public void setUp() {
}
}
In newer versions of Robolectric (tested with 4.3) your test class should look as follows:
@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowLog.class)
public class SandwichTest {
@Before
public void setUp() {
ShadowLog.setupLogging();
}
// tests ...
}
I would recommend using timber for your logging.
Though it will not log anything when running tests but it doesn't fail your tests unnecessarily the way android Log class does. Timber gives you a lot of convenient control over both debug and production build of you app.
If your are using the org.slf4j.Logger, then just mocking the Logger in test class using PowerMockito worked for me.
@RunWith(PowerMockRunner.class)
public class MyClassTest {
@Mock
Logger mockedLOG;
...
}