Say I have this situation:
interface Repository {
fun getMovies(success: (List<String>) -> Unit, failure: (Int) -> Unit)
}
and I want to mock the implementation of this interface. Basically in this case, there are two lambdas as input parameters to the getmovie method, and for the test case, I only want to produce success (success.invoke(theMoviesList) should be called).
Below is something similar to what I would like to do:
class MovieViewModel constructor(val repository: AppRepository) {
var movieNames = listOf<String>() // Not private, or live data, for simplicity
fun fetchMovies() {
repository.fetchMovies(
success = {
movies ->
this.movieNames = movies
}}, failure: {
statusCode ->
})
}
}
class MoviePageTests {
private var movieViewModel: MovieViewModel? = null
#Mock
lateinit var mockRepository: AppRepository
#Before
#Throws(Exception::class)
fun before() {
MockitoAnnotations.initMocks(this)
movieViewModel = MovieViewModel(repository = mockRepository)
}
#Test
fun checkFetchMoviesUpdatesMoviesData() {
var testMovies = listof("Dracula", "Superman")
//Set up mockito so that the repository generates success with testMovies above
?????
//
movieViewModel.fetchMovies()
assertEquals(movieViewModel.movies, testMovies)
}
}
I know how to do this by way of a RepositoryImpl, but not in Mockito, despite looking at many examples online.
Any ideas?
Related
I have used the memberExtensionProperties() method, but result collection of the extension properties is empty. The test code is attached. What is the right procedure?
class ExtensionPropertyTest {
class DummyClass{}
val DummyClass.id get() = 99
val DummyClass.name get() = "Joe"
#Test
fun testExtensionProperties() {
val dummyClass = DummyClass()
expect(dummyClass.id).toEqual(99) // OK
val properties = DummyClass::class.memberExtensionProperties
.stream()
.toList()
expect(properties).toHaveSize(2) // Fails due a zero size
}
}
memberExtensionProperties does not return extensions over a class, but its members that are at the same time extensions:
fun main() {
println(DummyClass::class.memberExtensionProperties)
}
class DummyClass {
val String.foo: Int
get() = toInt()
}
It is not that easy if at all possible to find all extensions over a class, because extensions are detached from their receivers and they can be located anywhere in the classpath.
We have some code to read data from DynamoDB:
suspend fun getKeys(owner: String): Set<String> {
...
val query = ...
query.subscribe { page -> foo(page) }.await()
return ...
}
The subscribe{} above is defined in AWS SDK:
default CompletableFuture<Void> subscribe(Consumer<T> consumer)
I'd like to unit test the logic in this function, for subscribe I don't care about it, hopefully just mock and do nothing.
I tried mock a callback in my unit test (irrelevant code removed):
class BucketRepoTest(
#Mock private val query: SdkPublisher<Page<DbBucket>>
) {
#Test
fun `get keys should be working`() {
val callback = mock<(Page<DbBucket>) -> Unit>()
val result = mock<CompletableFuture<Void>>()
whenever(query.subscribe(callback)).thenReturn(result)
runBlocking {
val keys = data.getKeys("Charlie")
assert(keys.isEmpty())
}
}
}
But when I run the test I got NPE:
query.subscribe { page -> foo(page) } must not be null
java.lang.NullPointerException: query.subscribe { page -> foo(page) } must not be null
at com.myApp.getKeys(myfile.kt:75)
at ...
Any idea how to fix it?
The result cannot be void.
val result = mock<CompletableFuture>()
try to create a mock data and pass as result
val result = the object or data type you want to more
Example
data Result( Charlie = "john", age = "23")
then
class BucketRepoTest(
#Mock private val query: SdkPublisher<Page<DbBucket>> ) {
#Test
fun `get keys should be working`() {
val callback = mock<(Page<DbBucket>) -> Unit>()
val result = Result()
whenever(query.subscribe(callback)).thenReturn(result)
runBlocking {
val keys = data.getKeys("Charlie")
assert(keys.isEmpty())
}
}
}
using kotlin, having code
fun fetchRemoteDataApi(): Single<RemoteDataResponse> = networkApi.getData()
// it is just a retrofit
#GET(".../api/getData")
fun getData() : Single<RemoteDataResponse>
fun mergeApiWithDb(): Completable = fetchRemoteDataApi()
.zipWith(localDao.getAll())
.flatMapCompletable { (remoteData, localData) ->
doMerge(remoteData, localData) //<== return a Completable
}
the code flow:
val mergeApiDbCall = mergeApiWithDb().onErrorComplete().cache() //<=== would like do some inspection at this level
PublishSubject.create<Unit>().toFlowable(BackpressureStrategy.LATEST)
.compose(Transformers.flowableIO())
.switchMap {
//merge DB with api, or local default value first then listen to DB change
mergeApiDbCall.andThen(listAllTopics())
.concatMapSingle { topics -> remoteTopicUsers.map { topics to it } }
}
.flatMapCompletable { (topics, user) ->
// do something return Completable
}
.subscribe({
...
}, { throwable ->
...
})
and when making the call
val mergeApiDbCall = mergeApiWithDb().onErrorComplete().cache()
the question is if would like to inspect on the Singles<RemoteDataResponse> returned from fetchRemoteDataApi() (i.e. using Log.i(...) to printout the content of RemoteDataResponse, etc.), either in got error or success case, how to do it?
/// the functions
fun listAllTopics(): Flowable<List<String>> = localRepoDao.getAllTopics()
// which a DAO:
#Query("SELECT topic FROM RemoteDataTable WHERE read = 1")
fun getAllTopics(): Flowable<List<String>>
///
private val remoteTopicUsers: Single<List<User>>
get() {
return Single.create {
networkApi.getTopicUsers(object : ICallback.IGetTopicUsersCallback {
override fun onSuccess(result: List<User>) = it.onSuccess(result)
override fun onError(errorCode: Int, errorMsg: String?) = it.onError(Exception(errorCode, errorMsg))
})
}
}
You cannot extract information about elements from the Completable. Though you can use doOnComplete() on Completable, it will not provide you any information about the element.
You can inspect elements if you call doOnSuccess() on your Single, so you need to incorporate this call earlier in your code. To inspect errors you can use doOnError() on both Completable or Single.
In Kotlin (and Java 8) we can use Lambda expression to remove boilerplate callback interface. For example,
data class Profile(val name: String)
interface ProfileCallback {
fun onSuccess(profile: Profile)
}
class ProfileRepository(val callback: ProfileCallback) {
fun getProfile() {
// do calculation
callback.onSuccess(Profile("name"))
}
}
We can change remove ProfileCallback and change it into Kotlin's Lambda:
class ProfileRepository(val callback: (Profile) -> Unit) {
fun getProfile() {
// do calculation
callback(Profile("name"))
}
}
This works fine, but I'm not sure how to mock and then verify that function. I have
tried using Mockito like this
#Mock
lateinit var profileCallback: (Profile) -> Unit
#Test
fun test() {
// this wouldn't work
Mockito.verify(profileCallback).invoke(any())
}
but it throw an Exception:
org.mockito.exceptions.base.MockitoException: ClassCastException
occurred while creating the mockito mock : class to mock :
'kotlin.jvm.functions.Function1', loaded by classloader :
'sun.misc.Launcher$AppClassLoader#7852e922'
How to mock and verify Lambda expression in Kotlin? Is it even possible?
Here is example how you can achieve that using mockito-kotlin:
Given repository class
class ProfileRepository(val callback: (Int) -> Unit) {
fun getProfile() {
// do calculation
callback(1)
}
}
Using mockito-kotlin lib - you can write test mocking lambdas like this:
#Test
fun test() {
val callbackMock: (Int) -> Unit = mock()
val profileRepository = ProfileRepository(callbackMock)
profileRepository.getProfile()
argumentCaptor<Int>().apply {
verify(callbackMock, times(1)).invoke(capture())
assertEquals(1, firstValue)
}
}
I am trying to implement a QueryBus. Basically, I want to register a list of QueryHandlers. Each QueryHandler implements a handle method defined by an interface. Each QueryHandler is associated to a Query. I want to be able to retrieve a QueryHandler using the Query and call handle on it.
The thing is the handle has to be generic because each QueryHandler handles a Query differently. They all take a dedicated Query and may return whatever they want.
interface Query<R>
interface QueryHandler<R, Q : Query<R>> {
fun handle(query: Q): R
fun listenTo(): String
}
// DTOs
data class BookDto(val name: String)
// List books query
data class ListBooksQuery(val page: Int = 1): Query<List<BookDto>>
class ListBooksQueryHandler: QueryHandler<List<BookDto>, ListBooksQuery> {
override fun handle(query: ListBooksQuery): List<BookDto> {
return listOf(BookDto("Dune"), BookDto("Dune II"))
}
override fun listenTo(): String = ListBooksQuery::class.toString()
}
// Get book query
data class GetBookQuery(val name: String): Query<BookDto?>
class GetBookQueryHandler: QueryHandler<BookDto?, GetBookQuery> {
override fun handle(query: GetBookQuery): BookDto {
return BookDto("Dune")
}
override fun listenTo(): String = GetBookQuery::class.toString()
}
// Run it!
fun main(args: Array<String>) {
// Initializing query bus
val queryHandlers = mapOf(
with(ListBooksQueryHandler()) {this.listenTo() to this},
with(GetBookQueryHandler()) {this.listenTo() to this}
)
val command = ListBooksQuery()
val result = queryHandlers[command::class.toString()].handle(command)
// Should print the list of BookDto
print(result)
}
I don't even know if its possible, to be honest.
UPDATE 1:
I changed the usage example in the main to show what I am really trying to do. The List was for (bad?) demonstration purpose. I want to store the QueryHandlers and retrieve them from a map.
Additional resources:
Here is what I really want to do:
https://gist.github.com/ValentinTrinque/76b7a32221884a46e657090b9ee60193
UPDATE I've read your gist and tried to come up with a solution that will provide a clean interface to the user of the QueryBusMiddleware.
Note that I used objects instead of classes for the QueryHandler implementations, which felt more natural to me (since there is only one possible entry in the map for each Query implementation).
interface Query<R>
interface QueryHandler<R, Q: Query<R>> {
fun handle(query: Q): R
fun listenTo(): String
}
// DTOs
data class BookDto(val name: String)
// List books query
data class ListBooksQuery(val page: Int = 1): Query<List<BookDto>>
object ListBooksQueryHandler: QueryHandler<List<BookDto>, ListBooksQuery> {
override fun handle(query: ListBooksQuery): List<BookDto> {
return listOf(BookDto("Dune"), BookDto("Dune II"))
}
override fun listenTo(): String = ListBooksQuery::class.toString()
}
// Get book query
data class GetBookQuery(val name: String): Query<BookDto?>
object GetBookQueryHandler: QueryHandler<BookDto?, GetBookQuery> {
override fun handle(query: GetBookQuery): BookDto {
return BookDto("Dune")
}
override fun listenTo(): String = GetBookQuery::class.toString()
}
// Run it!
fun main(args: Array<String>) {
// Initializing query bus
val queryHandlers = listOf(
ListBooksQueryHandler,
GetBookQueryHandler
)
val dispatcher: QueryBusMiddleware = QueryDispatcherMiddleware(queryHandlers)
// Calling query bus
val query = ListBooksQuery()
// Result should be List<BookDto>
val result = dispatcher.dispatch(query)
print(result)
}
interface QueryBusMiddleware {
fun <R, Q : Query<R>> dispatch(query: Q): R
}
class QueryDispatcherMiddleware constructor(handlers: List<QueryHandler<*, *>>) : QueryBusMiddleware {
private val handlers = HashMap<String, QueryHandler<*, *>>()
init {
handlers.forEach { handler -> this.handlers[handler.listenTo()] = handler }
}
override fun <R, Q : Query<R>> dispatch(query: Q): R {
val queryClass = query::class.toString()
val handler = handlers[queryClass] ?: throw Exception("No handler listen to the query: $queryClass")
return handler::class.members.find { it.name == "handle" }!!.call(handler, query) as R
}
}