I created a validation use case in which I'm validating the input using isDigitsOnly that use TextUtils internally.
override fun isDigitsOnly(size: String): Boolean {
return !size.trim().isDigitsOnly()
}
when I tried to test it, I got this error
Method isDigitsOnly in android.text.TextUtils not mocked
Does anyone know how I can mock the textUtils in my test class
#RunWith(MockitoJUnitRunner::class)
class ValidationInputImplTest {
#Mock
private lateinit var mMockTextUtils: TextUtils
private lateinit var validationInputImpl: ValidationInputImpl
#Before
fun setUp() {
validationInputImpl = ValidationInputImpl()
}
#Test
fun `contains only digits, returns success`() {
val input = "66"
val result = validationInputImpl(input)
assertTrue(result is ValidationResult.Success)
}
}
At the time of writing the answer,you cannot do that. Because the android code ,that you see is not actual code. You cannot create unit test for that. The default implementation of the methods in android.jar is to throw exception.
One thing you can do is, adding the below in build.gradle file.
But it make the android classes not to throw exception. But it will always return default value. So the test result may actually not work. It is strictly not recommended.
android {
testOptions {
unitTests.returnDefaultValues = true
}
}
The better way to do copy the code from android source and paste the file under src/test/java folder with package name as android.text .
Link to Answer
Related
I can't change project code, I can only write tests.
I have a function for testing:
fun funToBeTested() {
ClasWithWorkManager().veryVeryBadFunc(TAG)
// a lot of code to be tested below
...
}
and class with WorkManager:
class ClasWithWorkManager() {
companion object {
#JvmStatic
fun veryVeryBadFunc(tag: String) {
WorkManager.getInstance().cancelAllWorkByTag(tag)
WorkManager.getInstance().pruneWork()
}
}
}
There are no Android class references in the rest of this func.
Can I somehow cover with Unit test all funToBeTested() except of its first line?
Right now I have obvious WorkManager is not initialized properly. problem
The error I have:
The code with the error:
#RunWith(PowerMockRunner::class)
#PrepareForTest(PotatoProvider::class, PotatoConsumer::class)
class WantedButNotInvoked {
#Mock
lateinit var potatoConsumer: PotatoConsumer
#Test
fun potato() {
Observable.just(Potato()).subscribe(potatoConsumer)
verify(potatoConsumer).accept(Potato())
//verify(potatoConsumer).accept(any()) //-> This fails too with the same reason
}
}
data class Potato(val value: Int = 1)
class PotatoConsumer : Consumer<Potato> {
override fun accept(t: Potato?) {
println(t)
}
}
So I making subscribe with this mock(potatoConsumer), and the rxJava have called 'accept', and mockito mark it as interaction, but mockito thinks this interaction is not what I'm expecting, why?
Versions of libraries used her:
mockitoVersion = '2.8.9'
mockitoAndroidVersion = '2.7.22'
powerMockVersion="2.0.2"
kotlinMockito="2.1.0"
rxKotlin = "2.3.0"
rxJavaVersion = "2.2.10"
Kinda workaround
Some fields mocked by powermock, fails on 'verify', but fields mocked with mockito is not;
Mockito can't mock not opened fields, without mock-maker-inline, but mockito conflicts with Powermock mock-maker-inline;
Powermock can delegate calls of mock-maker-inline to other mock-maker-inline(https://github.com/powermock/powermock/wiki/PowerMock-Configuration)
Use Mockito.mock on the failed fields instead of #Mock/Powermock mock injection
Example of the "green" potato test method using PowerMockRunner
#Test
fun potato() {
potatoConsumer = mock() // <-
Observable.just(Potato()).subscribe(potatoConsumer)
verify(potatoConsumer).accept(potato)
}
I am not familiar with PowerMock but I tried this test and it passes:
#Test
fun potato() {
fakePotatoProvider = Mockito.mock(PotatoProvider::class.java)
potatoConsumer = Mockito.mock(PotatoConsumer::class.java)
`when`(fakePotatoProvider.getObservable()).thenReturn(Observable.just(Potato()))
fakePotatoProvider.getObservable().subscribe(potatoConsumer)
verify(potatoConsumer).accept(Potato())
}
Maybe because you aren't passing the same instance of Potato(). Try to refactor your code to this
#Test
fun potato() {
val testPotato = Potato()
`when`(fakePotatoProvider.getObservable()).thenReturn(Observable.just(testPotato))
fakePotatoProvider.getObservable().subscribe(potatoConsumer)
verify(potatoConsumer).accept(testPotato)
}
As I mentioned above, the reason why it might be failing is the constant creation of new instances when passing your Potato object, hance that comparison fails.
I'm stuck trying to mock some stuff with mockk:
I have the following setup on gradle
root:
|-- App (just a sample app for the SDK)
|-- SDK (SDK we develop) << apply plugin: 'com.android.library'
|-- SDKimpl.kt
|-- Foo (wrapper around a .jar library) << apply plugin: 'com.android.library'
|-- Foo.kt
So I'm writing an androidTest for the SDK and trying to mock Foo.kt.
There's nothing unusual about Foo class, just direct class Foo(private val someParams) {
So using androidTestImplementation "io.mockk:mockk-android:1.8.13" the mock goes:
val mock: Foo = mockk()
// val mock: Foo = mockkClass(Foo::class) // also tried this
every { mock.getData() } returns listOf("1", "2", "3")
I'm always getting the following crash:
io.mockk.MockKException: Missing calls inside every { ... } block.
at io.mockk.impl.recording.states.StubbingState.checkMissingCalls(StubbingState.kt:14)
at io.mockk.impl.recording.states.StubbingState.recordingDone(StubbingState.kt:8)
at io.mockk.impl.recording.CommonCallRecorder.done(CommonCallRecorder.kt:42)
Also tried just to gather information:
running inside JVM test folder. It gets mocked without issues, but I can't run my test as JVM
running androidTest inside Foo module. Got the same crash
using mockkClass(Foo::class). Got some crash
using annotation #MockK and MockKAnnotations.init(this). Got some crash.
added Log.d before every { line and inside getData() method and it seems the actual real method from the class is getting called during the mock setup. That seems super weird to me.
Any idea what's going wrong here?
edit:
as requested, full code. I'm current working on an isolated project to try to isolate the error, so Foo is just:
class Foo {
fun getData(): String {
Log.d(TAG, "invoked foo.getData()")
return "trolololo"
}
}
and then I have FooTest in androidTest:
class FooTest {
#Test
fun mock_foo() {
val foo = mockk<Foo>()
every { foo.getData() } returns "zero"
assertEquals("zero", foo.getData())
}
}
It seems to be a Mockk opened issue: https://github.com/mockk/mockk/issues/182
2 possible quick fixes (pick one):
Run the Instrumented Tests in an emulator >= Android-P
Set Foo class as open (and the method(s) you want to mock too)
Try to check the official guide and see what is missing.
In my case, I tried to mock an extension in Kotlin but missed the mockkStatic
fun Date.asMyTime() : DateTime = DateTime(this, DateTimeZone.getDefault())
mockkStatic("packageName.FileNameKt") // This is what I was missing
every {
DateTime().asMyTime()
} returns mock(DateTime::class.java)
In my case I forgot to spyk the class I was applying every {...} to. 😳
val presenter = spyk(MyPresenter())
every { view.myFun(any()) } returns Unit
In my case, I've missed
#Before
fun setUp() {
MockKAnnotations.init(this)
}
In my case I tried to mock using mock() function instead mockk() (double k)
Make sure the object is really a mock, not the real object.
For instance:
- Sdk sdk = Sdk()
+ Sdk sdk = mockk()
every { sdk.crypto } returns mockk()
My problem was that I used a java class without getters
public class KeyStorePasswordPair {
public KeyStore keyStore;
public String keyPassword;
public KeyStorePasswordPair(KeyStore keyStore, String keyPassword) {
this.keyStore = keyStore;
this.keyPassword = keyPassword;
}
}
I needed to add getters for the variables to make mockking work:
public class KeyStorePasswordPair {
public KeyStore getKeyStore() {
return keyStore;
}
public String getKeyPassword() {
return keyPassword;
}
private KeyStore keyStore;
private String keyPassword;
public KeyStorePasswordPair(KeyStore keyStore, String keyPassword) {
this.keyStore = keyStore;
this.keyPassword = keyPassword;
}
}
Mockk Missing calls inside every { ... } block
may also be thrown when you have an every block defined on an object that is not a mockk, for example:
You define stub behavior like this
every { foo.getData() } returns DATA
and then try to:
every { DATA.getVersion() } returns VERSION
Where MY_THING and VERSION objects are declared (and instantiated) in the test class.
The error message is not very informative and a bit misleading in that case.
try like this
`when`(mock.getData()).thenReturn(listOf("1", "2", "3"))
I'm doing my first deep dive into unit testing with Mockito, so please bear with me. I'm working on this test:
class PasswordStateManagerTest {
private lateinit var passwordStateManager: PasswordStateManager
#MockK
private lateinit var mockContext: Context
#MockK
private lateinit var mockSharedPreferences: SharedPreferences
#Before
fun setup() {
MockKAnnotations.init(this, true)
every{ mockContext.getApplicationSharedPreferences() } returns mockSharedPreferences
// this is the line that won't compile
Mockito.when(mockSharedPreferences.getBoolean("save_password", false)
)
.thenReturn(true)
passwordStateManager = PasswordStateManager(mockSharedPreferences)
}
}
The when.thenReturn line won't compile. It is expecting an open bracket { character where I am trying to execute on .thenReturn. As I read the docs, there is no place for an open bracket in this statement, so I must be off the rails.
Here is the part of the init method of the class being tested, which is what creates the need for the when-thenReturn line in the test:
init {
willSavePassword = prefs.getBoolean("save_password", false)
}
Thanks for any help (and patience while I get up to speed!).
This is because when is a reserved keyword in Kotlin, so the compiler is interpreting this as the beginning of a when statement. For example:
when (value) {
"value1" -> // do thing
}
To fix this, you can either escape the method name with backticks:
Mockito.`when`(mockSharedPreferences.getBoolean("save_password", false)).thenReturn(true);
Or, since you're using MockK anyway, just switch to another every:
every { mockSharedPreferences.getBoolean("save_password", false) } returns true
This simple unit test always passes and I can't figure out why.
#RunWith(JUnit4.class)
class SampleTest {
#Test testSomething() {
Uri uri = Uri.parse("myapp://home/payments");
assertTrue(uri == null);
}
}
What I have tried so far is using a "traditional" URI (http://example.com) but uri was also null.
I resolve this problem with Robolectric.
these are my unit test config
build.gradle
dependencies {
...
testImplementation 'junit:junit:4.12'
testImplementation "org.robolectric:robolectric:3.4.2"
}
test class
#RunWith(RobolectricTestRunner.class)
public class TestClass {
#Test
public void testMethod() {
Uri uri = Uri.parse("anyString")
//then do what you want, just like normal coding
}
}
kotlin
#RunWith(RobolectricTestRunner::class)
class TestClass {
#Test
fun testFunction() {
val uri = Uri.parse("anyString")
//then do what you want, just like normal coding
}
}
it works for me, hope this can help you.
Check if you have the following in your app's gradle file:
android {
...
testOptions {
unitTests.returnDefaultValues = true
}
Uri is an Android class and as such cannot be used in local unit tests, without the code above you get the following:
java.lang.RuntimeException: Method parse in android.net.Uri not mocked. See http://g.co/androidstudio/not-mocked for details.
The code above suppresses this exception and instead provides dummy implementations returning default values (null in this case).
The other option is that you are using some framework in your tests that provides implementations of the methods in Android classes.
Uri is Android class therefore it needs to be mocked before using in tests.
See this answer for example: https://stackoverflow.com/a/34152256/5199320
This was dumb...but I forgot to annotate my test class with: #RunWith(AndroidJUnit4::class)
Once I did that, everything worked as expected.
Finally I just changed my code to accept URIs as String, so now it works both in production and test and omit the usage of Uri.parse(). Now where an URI is needed I just use uri.toString() instead of parsing a String.
Below code solved my problem.
#RunWith(AndroidJUnit4::class)
#Config(sdk = [Build.VERSION_CODES.P])
class TestClass
Use URL class instead of Uri.
import java.net.URL
fun getDomain(url: String): String {
return URL(url).host
}
-------------
#Test
fun test() {
Assert.assertEquals("www.ru", getDomain("https://www.ru"))
}