mockk
mockk copied to clipboard
Bug: ClassCastException when using relaxed mock of generic type
Prerequisites
Please answer the following questions for yourself before submitting an issue.
- [x] I am running the latest version
- [x] I checked the documentation and found no answer
- [x] I checked to make sure that this issue has not already been filed
Description
I might have run into a MockK limitation trying to use relaxed mock of a generic type (particularly Spring's JpaRepository). When using such mock object, my test throws a ClassCastException at the point where the test code calls the method of the mock object that returns an object using the type parameter.
For example, if have the following types,
interface BaseRepository<T> {
fun save(obj: T): T
}
interface BookRepository: BaseRepository<Book>
and I declare a relaxed mock for BookRepository, the test throws a ClassCastException during the invocation of save(book).
Steps to Reproduce
I have prepared a demo code base to illustrate this issue. Please check the last 3-4 commits as I experimented a bit to confirm that this issue doesn't only affect Spring's JpaRepository but in general, any type (interface) with similar declaration.
same problem here (mockk 1.9.3). Small example code to reproduce the problem:
interface BaseRepository<T> {
fun save(obj: T): T
}
interface IntRepository: BaseRepository<Int>
fun main(args: Array<String>) {
val a = mockk<IntRepository>(relaxed = true)
a.save(1)
}
Results in:
Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.Integer
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. If you are sure that this issue is important and should not be marked as stale just ask to put an important label.
I am experiencing the same issue. I am trying to mock the following function:
inline fun <reified T, reified U> post(uri: String, body: T): U? = webClient
.post()
.uri(uri)
.body(Mono.just(body), T::class.java)
.retrieve()
.bodyToMono(U::class.java)
.block()
like this:
every { httpClient.post<Map<String, Any>, User>(any(), hashMapOf()) } returns createUser(email = email)
but get the same error as above:
java.lang.Object cannot be cast to User. I am using version 1.9.3 of mockk and a relaxed mock of the httpClient.
Oddly enough, mocking this function works fine:
inline fun <reified T> getMono(uri: String): Mono<T> = webClient
.get()
.uri(uri)
.retrieve()
.bodyToMono(T::class.java)
.block()
Is there any update on this?
I am facing the same issue. I am using a Result wrapper for my network models.
sealed class Result<out R> {
data class Success<out T>(val data: T) : Result<T>()
data class Error(val exception: Failure) : Result<Nothing>()
}
However, I am getting;
java.lang.ClassCastException: java.lang.Object cannot be cast to mypath.data.MyResponse
Try to use hint. check docs
пт, 6 мар. 2020 г., 10:54 Nuh Koca [email protected]:
I am facing the same issue. I am using a result wrapper for my network models.
sealed class Result<out R> { data class Success<out T>(val data: T) : Result<T>() data class Error(val exception: Failure) : Result<Nothing>() }
However I am getting;
java.lang.ClassCastException: java.lang.Object cannot be cast to mypath.data.MyResponse
— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub https://github.com/mockk/mockk/issues/321?email_source=notifications&email_token=ABIBXDCOKW4TGW5467MUOWLRGDB6HA5CNFSM4HZ6V6O2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEOAYQSQ#issuecomment-595691594, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABIBXDH5GI2W4JXSGPFYDNTRGDB6HANCNFSM4HZ6V6OQ .
@oleksiyp thanks it seems useful however I am having one question.
Because of this generic type, whenever I want to access a val of my data class, I am getting;
io.mockk.MockKException: no answer found for: MyResponse(myResponse#2).getMyVal()
How can I fix this with hint? I am using it like;
coEvery {
service.fetchPolygons(
any(),
capture(maxSpeedSlot),
any(),
any()
).hint(Result::class)
} answers {
myResponse
}
but it didn't work. In addition, I'd like to assert multiple values. In case of that, should I use multiple hints?
Thanks!
Hint should be before call
пт, 6 мар. 2020 г., 11:26 Nuh Koca [email protected]:
@oleksiyp https://github.com/oleksiyp thanks it seems useful however I am having one question.
Because of this generic type, whenever I want to access a val of my data class, I am getting;
io.mockk.MockKException: no answer found for: MyResponse(myResponse#2).getMyVal() How can I fix this with hint? I am using it like;
coEvery { service.fetchPolygons( any(), capture(maxSpeedSlot), any(), any() ).hint(Result::class) } answers { myResponse }
but it didn't work. Thanks!
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/mockk/mockk/issues/321?email_source=notifications&email_token=ABIBXDCDAQJ6QD5CFFGEJVLRGDFU3A5CNFSM4HZ6V6O2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEOA3TKY#issuecomment-595704235, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABIBXDCQMSKZ6GGNBMWBBVTRGDFU3ANCNFSM4HZ6V6OQ .
Ok, it is working. So do I have to use hint per assertion? I mean I'd like to assert 3 conditions so that I need to create coEvery per condition, right?
I am facing the same issue. I am using a
Resultwrapper for my network models.sealed class Result<out R> { data class Success<out T>(val data: T) : Result<T>() data class Error(val exception: Failure) : Result<Nothing>() }However, I am getting;
java.lang.ClassCastException: java.lang.Object cannot be cast to mypath.data.MyResponse
hey @nuhkoca did you manage to resolve that issue?
@yavski for most cases I fixed this switching to Coroutines and wrapping Result in Flow instead. Then I am able to easily test my flow with cashapp's turbine. Other than that, it wasn't working last time but I will try and let you know.
https://github.com/cashapp/turbine
Any updates on this by any chance?
I have hundreds of tests failing because of this issues so unfortunately using hint() on all of them would be an enormous effort.
Type inference was somehow working on Java 8 (I only have this issue when switching to Java 11).
This is possibly related to https://github.com/mockk/mockk/issues/606 as well?
I solved it as below
every { repository.save<ReturnTypecClass>(any()) } returns ReturnTypeClass
I am wrong assuming that the intention when we use mockk(relaxed = true) is to avoid writing a default answer where every call is done?
@oleksiyp is there anything we can do to get a workaround for this by any chance?
On large codebases this is causing hundreds of tests failures which would be really hard to fix one by one with hint.
Thank you in advance for looking into it! 🙏
any update on this??
Hey guys, it's blocking me and my team from updating to Android Studio 4.2 since it requires Java 11 and our codebase has a lot of tests in mockk that throw this exception on Java 11. Could you give an estimate when it's going to be fixed?
Hello, any update on this? 🙏
Hey guys, a quick light on this issue
I was facing the problem when I was writing unit tests where one of mocked parameters was called at my ViewModel class.
I fixed this by creating mocks with every/coEvery of the mocked object functions that were called at the time of the crash.
As it is quite common to test with the JpaRepository pattern, this issue is quite impactful.
Still have this issue even if relaxed == false. But now i have Result<Boolean> instead of Result<MyClass>
Not able to mock repository beans, get this error with save calls.
Tried to use hint and relaxed, still same problem.
I also have the same issue. It's very unfortunate.