I'm kind of new with mockito so I don't know if this behavior is normal or not.
This is a simple example class:
open class Example( val example2: Example2) {
fun getStuff(fileName: String) : String {
return example2.getFileExtension(fileName)
}
}
open class Example2(val fileUtils: FileUtils) {
fun getFileExtension(fileName: String): String {
return fileUtils.getExtension(fileName)
}
}
So when I tried to test it with this code:
class ExampleTest {
lateinit var example: Example
val example2 = mock(Example2::class.java)
#Before
fun init() {
example = Example(example2)
}
#Test
fun getFileExtensionTest() {
val resultExpected = "jpg"
`when`(example2.getFileExtension(ArgumentMatchers.anyString())).thenReturn("jpg")
assertThat(example.getStuff("hello.jpg"), `is`(resultExpected))
}
}
I'm getting a NullPointerException in return fileUtils.getExtension(fileName) when 'when' is executed
So 'when' is executing the function that suppose to be mocked.
I got it!!
Kotlin makes a function final by default and mockito can't mock final functions/classes. So there are two solutions here:
Add open to the functions
Or make mockito use the final classes and functions : Mockito 2 now supports final
Related
I'm using MockK as mock library for kotlin, for some reason I get an error on the every{} line.
io.mockk.MockKException: Missing calls inside every { ... } block.
this is my test class
internal class TestClass {
private val service: MyService = mockk(relaxed = true)
private val dao: MyDao = mockk(relaxed = true)
private val repository: MyRepository = MyRepositoryImp(
service = service,
dao = dao,
)
#Test
fun test() = runBlocking {
val expected = SOMETHING
every { repository.getValues() } returns flowOf(SOMETHINGELSE) // crash
assertEquals(1, 1)
}
}
MyService is just a retrofit interface
internal interface MyService {
#GET("123")
suspend fun getRemoteData(): Response<SOMECLASS>
}
Anyone has any idea? thanks!
You've instantiated the repository as a real instance. That means that it'll actually execute the code inside its implementation and that's known as the 'subject under test'.
But every { ... } returns ... needs a mockk instance.
So in your example, it should be
every { service.getRemoteData() } returns ...
or
every { dao.someMethod() } returns ...
I have an issue in unit test for a function used Coroutine to call API with networkBoundResource.
The issue is when run the test the API actually called, although it's supposed to return the expected response such as I determined in this line: whenever(mfSDKPaymentRepository.sendPayment(request)).thenReturn(expectedResponse)
This is the function want to test:
fun callSendPayment(
coroutineScope: CoroutineScope? = GlobalScope,
request: MFSendPaymentRequest,
apiLang: String,
listener: (MFResult<MFSendPaymentResponse>) -> Unit
) {
Const.apiLang = apiLang
coroutineScope?.launch(Dispatchers.Main) {
val dataResource = networkBoundResource {
mInteractors.sendPayment(request)
}
when (dataResource) {
is MFResult.Success ->
listener.invoke(MFResult.Success(dataResource.value.response!!))
is MFResult.Fail ->
listener.invoke(MFResult.Fail(dataResource.error))
}
}
}
This is the test class:
class MFSDKMainTest {
private val mfSDKPaymentRepository = mock<MFSDKPaymentGateWay>()
private val testScope = TestCoroutineScope()
#get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
#Before
fun setup() {
Dispatchers.setMain(Dispatchers.Unconfined)
}
#After
fun tearDown() {
Dispatchers.resetMain()
testScope.cleanupTestCoroutines()
}
#Test
fun testCallSendPayment() = runBlockingTest {
val data = MFSendPaymentResponse(invoiceId = ID)
val expectedResponse = SDKSendPaymentResponse(data)
val request = MFSendPaymentRequest(
0.100,
"Customer name",
MFNotificationOption.LINK
)
val lang = MFAPILanguage.EN
whenever(mfSDKPaymentRepository.sendPayment(request))
.thenReturn(expectedResponse)
MFSDKMain.callSendPayment(testScope, request, lang) {
assert(it is MFResult.Success)
}
}
}
In your function when you call
mInteractors.sendPayment(request)
how do you get a refrence to MFSDKPaymentGateWay? In your test method you are not setting the mocked object in MFSDKMain class.
If you are creating it inside the same class (which i assume is an Object class) you may need to find a way to mock object class. Probably you need to use mockk instead of mockito
If you have setter method for MFSDKPaymentGateWay you should call it in your test method.
I'm trying to make some unit tests for my business logic.
I have repository in which I save to room database (2.1.0-rc01) some data from response.
Data saving into different tables with different dao in single transaction.
Code is simplified:
ItemRepository
suspend fun saveItems(response: Response) {
val items = response.items.map { it.toLocalItem() }
val subItems = response.items.flatMap { item ->
item.subItems.map { it.toLocal(item.id) }
}
db.withTransaction {
db.itemDao().deleteAll()
db.itemDao().insertAll(items)
db.subItemDao().insertAll(subItems)
}
}
For unit test I'm using Mockk library. How can I mock room withTransaction method?. withTransaction is declared as
suspend fun <R> RoomDatabase.withTransaction(block: suspend () -> R): R
I'm trying to writing test
#MockK
private lateinit var database: AppDatabase
#MockK
private lateinit var itemDao: ItemDao
#MockK
private lateinit var subItemDao: SubItemDao
#Test
fun checkSaveItems() = runBlocking {
repository = ItemRepository(database)
coEvery { database.itemDao() } returns itemDao
coEvery { database.subItemDao() } returns subItemDao
//TODO: execute database.withTransaction(block: suspend () -> R)
coEvery { itemDao.deleteAll() } just Runs
coEvery { itemDao.insertAll(any()) } just Runs
coEvery { subItemDao.insertAll(any()) } just Runs
repository.saveItems(testResponse)
coVerifySequence {
itemDao.deleteAll()
itemDao.insertAll(testItems)
subItemDao.insertAll(testSubItems)
}
}
You first have to enable static mocks for the Android Room KTX method withTransaction {}. You also need to capture the suspend lambda function passed to it. This captured function can just be invoked so the code inside it runs. Since you're mocking all the database calls, you don't need a real transaction here.
#Before
fun initMocks() {
MockKAnnotations.init(this)
mockkStatic(
"androidx.room.RoomDatabaseKt"
)
val transactionLambda = slot<suspend () -> R>()
coEvery { db.withTransaction(capture(transactionLambda)) } coAnswers {
transactionLambda.captured.invoke()
}
}
You should then be able to run your code as written.
To expand on Andrew's answer, the mockk documentation for extension functions shows that if you are mocking an object wide or class wide extension function, you can just use regular mockk to achieve that. However, if you are using a module wide extension function, like withTransaction, you also need to perform mockkStatic on the module's class name.
I have a file that references some static methods:
class MyViewModel {
fun test() { }
companion object {
private val MY_STRING = ResourceGrabber.grabString(R.string.blah_blah)
}
}
In my JUnit test for this file, I write some code to mock my resource grabber in setup. This compiles and runs, and the following test fails as I'd expect it to:
#PrepareForTest(ResourceGrabber::class)
#RunWith(PowerMockRunner::class)
class MyViewModelTest {
private lateinit var viewModel: MyViewModel
#Before
fun setup() {
PowerMockito.mockStatic(ResourceGrabber::class.java)
val mockResourceGrabber = Mockito.mock(ResourceGrabber::class.java)
whenever(mockResourceGrabber.grabString(Mockito.anyInt())).thenAnswer { invocation ->
val res: Int? = invocation?.arguments?.get(0) as? Int
TestResourceGrabber.grabString(res)
}
viewModel = MyViewModel()
}
#Test
fun someTest() {
// Fails, as expected.
assertEquals(2, 3)
}
}
Here is where things get weird. I recently learned about custom JUnit rules that you can use to avoid some duplicated code across tests. In this case, I don't want to have to copy and paste my resource grabber work into every single test suite that uses it, so I made a custom rule:
class ResourceGrabberRule : TestRule {
override fun apply(base: Statement?, description: Description?): Statement {
return object : Statement() {
override fun evaluate() {
PowerMockito.mockStatic(ResourceGrabber::class.java)
val mockResourceGrabber = Mockito.mock(ResourceGrabber::class.java)
whenever(mockResourceGrabber.grabString(Mockito.anyInt())).thenAnswer { invocation ->
val res: Int? = invocation?.arguments?.get(0) as? Int
TestResourceGrabber.grabString(res)
}
}
}
}
}
Below is the implementation of that. The crazy thing is that now EVERY test is passing no matter what:
#PrepareForTest(ResourceGrabber::class)
#RunWith(PowerMockRunner::class)
class MyViewModelTest {
private lateinit var viewModel: MyViewModel
#Rule
#JvmField
val resourceGrabber = ResourceGrabberRule()
#Before
fun setup() {
viewModel = MyViewModel()
}
#Test
fun someTest() {
// PASSES!!!?!?!?!?!
assertEquals(2, 3)
}
}
I'm not sure where the problem lies. I've tried building and running tests from both Android Studio and the command line. I don't know if I've implemented my rule incorrectly, or if it's an issue with the JUnit Rule connected with Powermock, or if it's an issue with Kotlin annotation processing. The tests compile and run but just pass no matter what's inside the tests themselves.
I'm open to comments about the architecture here (I'm sure the community has plenty) but I'm really looking for an explanation as to why the rule I wrote passes every test.
In your custom TestRule, you need to call base.evaluate() to continue the chain of rules https://github.com/junit-team/junit4/wiki/rules#custom-rules
I successfully did some tests of asynchronous function with only one callback interface as parameter with mockito-kotlin library but when I try to do a test of same function with another parameter like a String or Integer I receive error:
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Invalid use of argument matchers!
2 matchers expected, 1 recorded:
-> at com.example.presentation.presenter.MyCollectionPresenterTest.getComicListByHeroOK(MyCollectionPresenterTest.kt:97)
This exception may occur if matchers are combined with raw values:
//incorrect:
someMethod(anyObject(), "raw String");
When using matchers, all arguments have to be provided by matchers.
For example:
//correct:
someMethod(anyObject(), eq("String by matcher"));
For more info see javadoc for Matchers class.
I´m sure I´m mocking properly callback interface with any() but I don´t know if I´m mocking integer parameter correctly. I tried with any(), anyInt(), eq(1) and any() as Int but always the same error.
Here is the class that I want to test:
#PerFragment
class MyCollectionPresenter #Inject constructor(private val useCase: GetComicListByHeroUseCase) {
#Inject
lateinit var view: MyCollectionView
lateinit var models: List<ComicModel>
fun getComicListByHero(heroId: Int) {
useCase.execute(heroId, object : HeroUseCase.GetComicListByHeroCallback {
override fun onComicListReceived(comicList: List<Comic>) {
models = ComicModelMapper.toModel(comicList)
view.setItems(models)
}
override fun onError() {
view.showMessage()
}
})
}
}
And this is the test class:
class MyCollectionPresenterTest : UnitTest() {
private lateinit var presenter: MyCollectionPresenter
#Mock
private lateinit var useCase: GetComicListByHeroUseCase
#Mock
private lateinit var view: MyCollectionView
#Before
fun setUp() {
presenter = MyCollectionPresenter(useCase)
initializeView()
}
#Test
fun getComicListByHeroOK() {
setupGetComicsCallbackOK()
presenter.getComicListByHero(any())
verify(presenter.view).setItems(emptyList())
}
#Test
fun getComicListByHeroError() {
setupGetComicsCallbackError()
presenter.getComicListByHero(any())
verify(presenter.view).showMessage()
}
private fun initializeView() {
presenter.view = view
}
private fun setupGetComicsCallbackError() {
doAnswer {
val callback = it.arguments[0] as HeroUseCase.GetComicListByHeroCallback
callback.onError()
null
}.`when`(useCase).execute(any(), any())
}
private fun setupGetComicsCallbackOK() {
doAnswer {
val callback = it.arguments[0] as HeroUseCase.GetComicListByHeroCallback
callback.onComicListReceived(emptyList())
null
}.`when`(useCase).execute(any(), any())
}
}
This is base unit test class:
#RunWith(MockitoJUnitRunner::class)
abstract class UnitTest {
#Suppress("LeakingThis")
#Rule
#JvmField
val injectMocks = InjectMocksRule.create(this#UnitTest)
}
And this is a class for inject mocks rule:
class InjectMocksRule {
companion object {
fun create(testClass: Any) = TestRule { statement, _ ->
MockitoAnnotations.initMocks(testClass)
statement
}
}
}
Thank you very much for your help and excuse my english.
Regards!
UPDATE: I found the solution and posted as answer.
Finally, I know what I was doing wrong. First at all, it.argument[1] because callback is the second parameter of the function that I want to mock the answer.
And the other thing is that I was mocking the parameter of the function that I want to test presenter.getComicListByHero(1).
Here is the revised code:
#Test
fun getComicListByHeroError() {
setupGetComicsCallbackError()
presenter.getComicListByHero(1)
verify(presenter.view).showMessage()
}
private fun setupGetComicsCallbackError() {
doAnswer {
val callback = it.arguments[1] as HeroUseCase.GetComicListByHeroCallback
callback.onError()
null
}.`when`(useCase).execute(ArgumentMatchers.anyInt(), any())
}
Thank you very much to #voghDev for his help