How to use MockK to mock an observable - rx-kotlin

I have a data provider that has an Observable<Int> as part of the public API. My class under test maps this into a Observable<String>.
How do I create a mock so that it can send out different values on the data provider's observable?
I can do it using a Fake object, but that is a lot of work that I don't think is necessary with MockK.
Simplified code:
interface DataProvider {
val numberData:Observable<Int>
}
class FakeDataProvider():DataProvider {
private val _numberData = BehaviorSubject.createDefault(0)
override val numberData = _numberData.hide()
// Note: the internals of this class cause the _numberData changes.
// I can use this method to fake the changes for this fake object,
// but the real class doesn't have this method.
fun fakeNewNumber( newNumber:Int ) {
_numberData.onNext( newNumber )
}
}
interface ClassUnderTest {
val stringData:Observable<String>
}
class MyClassUnderTest( dataProvider: DataProvider ):ClassUnderTest {
override val stringData = dataProvider.numberData.map { "string = " + it.toString() }
}
class MockKTests {
#Test fun testUsingFakeDataProvider() {
val fakeDataProvider = FakeDataProvider()
val classUnderTest = MyClassUnderTest( fakeDataProvider )
val stringDataTestObserver = TestObserver<String>()
classUnderTest.stringData.subscribe( stringDataTestObserver )
fakeDataProvider.fakeNewNumber( 1 )
fakeDataProvider.fakeNewNumber( 2 )
fakeDataProvider.fakeNewNumber( 3 )
// Note we are expecting the initial value of 0 to also come through
stringDataTestObserver.assertValuesOnly( "string = 0", "string = 1","string = 2","string = 3" )
}
// How do you write the mock to trigger the dataProvider observable?
#Test fun testUsingMockDataProvider() {
val mockDataProvider = mockk<DataProvider>()
// every { ... what goes here ... } just Runs
val classUnderTest = MyClassUnderTest( mockDataProvider )
val stringDataTestObserver = TestObserver<String>()
classUnderTest.stringData.subscribe( stringDataTestObserver )
// Note we are expecting the initial value of 0 to also come through
stringDataTestObserver.assertValuesOnly( "string = 0", "string = 1","string = 2","string = 3" )
}
}

Try to use following:
every { mockDataProvider.numberData } answers { Observable.range(1, 3) }
And maybe you need to use another way to make a mock object, like this:
val mockDataProvider = spyk(DataProvider())

Do something like this where we create an observable fakelist of the observable
var fakeList :List<Quiz> = (listOf<Quiz>(
Quiz("G1","fromtest","","",1)
))
var observableFakelist = Observable.fromArray(fakeList)
you can then return your observableFakelist.

Related

Nearly equal Unit tests behave differently

I wrote an unit test to test if a Map is updated every second. In the code I inserted some println statements to see if the function really is called. The test runs indefinitely until I abort the test, showing the println in system.out. So the function works correctly, but my test does not.
Testcode and programcode are below.
#Test
fun test_count_is_updated_using_turbine(){
runTest {
testScheduler.runCurrent()
viewmodelUnderTest.runningTaskMap.test(2.seconds){
val firstCount = awaitItem()
assertThat(firstCount).containsKey(1)
viewmodelUnderTest.startUpdates()
val secondCount = awaitItem()
viewmodelUnderTest.stopUpdates()
cancelAndIgnoreRemainingEvents()
assertThat(firstCount).isEqualTo(0)
assertThat(secondCount).isEqualTo(1)
}
}
}
Program code in viewmodel:( RunningMapState is initialized with a value when the viewmodel is initialized, so it is not an empty map.)
var runningTaskState:Map<Int, RunningTaskView> = mapOf()
private val _runningTaskMap = MutableStateFlow<Map<Int, RunningTaskView>>(mapOf())
val runningTaskMap: StateFlow<Map<Int, RunningTaskView>>
get()=_runningTaskMap
suspend fun updateRunningTasks() {
if (runningTaskState.isNotEmpty()) {
println("Before update:"+runningTaskState)
runningTaskState.forEach {
it.value.duration += 1000L
}
println("After update:"+runningTaskState)
//val tempMap = runningTaskState.toMap()
_runningTaskMap.update{ runningTaskState }
}
else{
println("map empty")
}
}
fun startUpdates(){
val previousJob = timerJob
timerJob = viewModelScope.launch (dispatcher){
previousJob?.cancelAndJoin()
while (isActive) {
delay(1000L)
updateRunningTasks()
}
}
}
fun stopUpdates(){
viewModelScope.launch(dispatcher) {
timerJob?.cancelAndJoin()
}
}
To test the testcode I wrote a sample program replacing the map with a Integer. This test functions as expected and passes. Viewmodelcode and testcode are nearly identical.
See below code:
#Test
fun test_count_is_updated_using_turbine(){
runTest {
testDispatcher.scheduler.runCurrent()
viewmodelUnderTest.countFlow.test(2.seconds){
val firstCount = awaitItem()
val started = viewmodelUnderTest.startTimer()
val secondCount = awaitItem()
viewmodelUnderTest.stoptimer()
cancelAndIgnoreRemainingEvents()
assertThat(started).isTrue()
assertThat(firstCount).isEqualTo(0)
assertThat(secondCount).isEqualTo(1)
}
}
}
Viewmodel:
var count = 0
var countFlow = MutableStateFlow<Int>(0)
fun updateTimer(){
count++
countFlow.update { count }
println("count updated to $count")
}
fun stoptimer(){
viewModelScope.launch {
timerJob?.cancelAndJoin()
}
}
fun startTimer():Boolean{
val previousJob = timerJob
timerJob = viewModelScope.launch(dispatcher) {
previousJob?.cancelAndJoin()
println("start updating")
while(isActive) {
delay(1000L)
println("calling update")
updateTimer()
println("update called")
}
}
println("TimerTest is active: "+timerJob?.isActive?.toString()?:"not initialized")
return timerJob?.isActive?:false
}
Can anyone explain why the first test is not finishing?

What's the right way to actually get this search functionality to work?

I have this app which displays a list of "coins" to the users . This list was parsed from an JSON API and I used Jetpack Compose for the UI. I implemented
Here is the code of the Jetpack composable list of "coins"
#Composable
fun CoinListScreen(
navController: NavController,
viewModel: CoinListViewModel = hiltViewModel(),
) {
val state = viewModel.state.value
Surface {
Box(modifier = Modifier.fillMaxSize()) {
Column {
androidx.compose.foundation.Image(painter = painterResource(id = R.drawable.ic_baseline_currency_bitcoin_24),
contentDescription = "BTC",
modifier = Modifier
.fillMaxWidth()
.align(CenterHorizontally)
.size(50.dp, 50.dp)
)
SearchBar(
hint = "Search..",
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
){
viewModel.searchCoinsList(it) **//here I'm calling my search function from the view model, inside my search bar**
}
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(state.coins) { coin ->
Spacer(modifier = Modifier.height(5.dp))
CoinListItem(
coin = coin,
onItemClick = {
navController.navigate(Screen.CoinDetailScreen.route + "/${coin.id}")
}
)
Divider()
}
}
}
if (state.error.isNotBlank()) {
Text(
text = state.error,
color = MaterialTheme.colors.error,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp)
.align(Alignment.Center)
)
}
if (state.isLoading) {
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
}
}
}
}
**//and this Is my composable search bar**
#Composable
fun SearchBar(
modifier: Modifier = Modifier,
hint: String = "",
onSearch: (String) -> Unit = {}
) {
var text by remember {
mutableStateOf("")
}
var isHint by remember {
mutableStateOf(hint != "")
}
Box(modifier = modifier){
BasicTextField(
value = text,
onValueChange = {
text = it
onSearch(it)
},
maxLines = 1,
singleLine = true,
modifier = Modifier
.fillMaxWidth()
.shadow(5.dp, CircleShape)
.background(Color.White, CircleShape)
.padding(horizontal = 20.dp, vertical = 12.dp)
.onFocusChanged {
isHint = it.isFocused != true
}
)
if(isHint){
Text(
text = hint,
color = Color.LightGray,
modifier = Modifier.padding(horizontal = 20.dp, vertical = 12.dp)
)
}
}
}
and this is my view model, this is where I'm implementing the search function, this is where I'm lost, variables that I'm searching for are name, rank, and symbol from the Coin domain list
#HiltViewModel //injecting the use case
class CoinListViewModel #Inject constructor (
private val getCoinsUseCase: GetCoinsUseCase,
) : ViewModel() {
//vmstate Live Template, only the view model touches it
private val _state =
mutableStateOf(CoinListState())
val state: State<CoinListState> = _state
**//for search purposes , this is where I'm lost**
private var coinsList = mutableStateOf<List<Coin>>(listOf())
private var cachedCoinsList = listOf<Coin>()
private var isSearchStarting = true
private var isSearching = mutableStateOf(false)
init {
getCoins()
}
**//for search purposes , this is where I'm lost**
fun searchCoinsList(query: String){
val listToSearch = if(isSearchStarting){
coinsList.value
} else {
cachedCoinsList
}
viewModelScope.launch(Dispatchers.Default) {
if(query.isEmpty()){
coinsList.value = cachedCoinsList
isSearching.value = false
isSearchStarting = true
return#launch
}
val results = listToSearch.filter {
//val iterate: Int = coins.size
it.name.contains(query.trim(), ignoreCase = true) ||
(it.rank.toString() == query.trim()) ||
it.symbol.contains(query.trim(), ignoreCase = true)
}
if(isSearchStarting){
cachedCoinsList = coinsList.value
isSearchStarting = false
}
coinsList.value = results
isSearching.value = true
}
}
//function that calls our GetCoinsUseCase and puts the data inside the state object
//to display that in the UI
private fun getCoins() {
//overwrote the invoke function earlier for the use case which allows us to call the use case as a function
getCoinsUseCase().onEach { result ->
when (result) {
is Resource.SUCCESS -> {
_state.value =
CoinListState(coins = result.data ?: arrayListOf())
}
is Resource.ERROR -> {
_state.value =
CoinListState(
error = result.message ?: "An unexpected error occurred"
)
}
is Resource.LOADING -> {
_state.value = CoinListState(isLoading = true)
}
}
}.launchIn(viewModelScope)
}
}
CoinsListState data class used in view model
data class CoinListState(
val isLoading: Boolean = false,
val coins: ArrayList<Coin> = arrayListOf(),
val error: String = ""
)
this is my "GetCoinsUseCase" to get the coins
class GetCoinsUseCase #Inject constructor(
private val repository: CoinRepository
) {
// overwriting the operator fun invoke allows us to call the use case
//GetCoinsUseCase as if it was a function, and we return a flow because
// we want to emit states LOADING -> for progress bar, SUCCESS -> attach list of coins,
// and ERROR
operator fun invoke(): kotlinx.coroutines.flow.Flow<Resource<ArrayList<Coin>>> = flow {
try {
emit(Resource.LOADING<ArrayList<Coin>>())
//we mapped it to toCoin because we returning a list of coin, not coinDTO
val coins = repository.getCoins().map { it.toCoin() }
emit(Resource.SUCCESS<ArrayList<Coin>>(coins as ArrayList<Coin>))
}catch (e: HttpException){
emit(Resource.ERROR<ArrayList<Coin>>(e.localizedMessage ?: "An unexpected error occurred"))
}catch (e: IOException){
emit(Resource.ERROR<ArrayList<Coin>>("Couldn't reach server. Check connection"))
}
}
}
just the coin repository that is implemented in another place
interface CoinRepository {
//repository definitions
suspend fun getCoins() : ArrayList<CoinDTO>
suspend fun getCoinById(coinId: String) : CoinDetailDTO
}
This is my domain - Domain - only contains the data needed
data class Coin(
var id: String,
var isActive: Boolean,
var name: String,
var rank: Int,
var symbol: String
)
and this is how I'm mapping it
data class CoinDTO(
val id: String,
#SerializedName("is_active")
val isActive: Boolean,
#SerializedName("is_new")
val isNew: Boolean,
val name: String,
val rank: Int,
val symbol: String,
val type: String
)
fun CoinDTO.toCoin(): Coin {
return Coin(
id = id,
isActive = isActive,
name = name,
rank = rank,
symbol = symbol,
// logo = CoinDetailLogo(logo = String()).logo
)
}
Coin list item if needed for reference, this is what is displayed to the user in the list
#Composable
fun CoinListItem (
coin: Coin,
onItemClick: (Coin) -> Unit
) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { onItemClick(coin) }
.padding(20.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = "${coin.rank}. ${coin.name} (${coin.symbol})",
style = MaterialTheme.typography.body1,
overflow = TextOverflow.Ellipsis
)
Text(
text = if(coin.isActive) "active" else "inactive",
color = if(coin.isActive) Color.Green else Color.Red,
fontStyle = FontStyle.Italic,
textAlign = TextAlign.End,
style = MaterialTheme.typography.body2,
modifier = Modifier.align(CenterVertically)
)
}
}
as well as the "Resource" generic for states
//UIStates
sealed class Resource<T>(val data: T? = null, val message: String? = null) {
class SUCCESS<T>(data: T) : Resource<T>(data)
class ERROR<T>(message: String, data: T? = null) : Resource<T>(data, message)
class LOADING<T>(data: T? = null) : Resource<T>(data)
}
again, given this info, how can I get the function searchCoinList in the view model to correctly view the searched data (name, rank, or symbol) when it is called in the CoinListScreen inside the Search Bar. Thank you so much
It seems like you want to implement a basic instant search functionality. It's pretty easy to achieve using Kotlin's StateFlow and its operators. Consider the following implementation with description:
// CoinListViewModel
private val queryFlow = MutableStateFlow("")
private val coinsList = mutableStateOf<List<Coin>>(listOf())
init {
queryFlow
.debounce(300) // filters out values that are followed by the newer values within the given timeout. The latest value is always emitted.
.filterNot { query -> userInput.isEmpty() } // filter the unwanted string like an empty string in this case to avoid the unnecessary network call.
.distinctUntilChanged() // to avoid duplicate network calls
.flowOn(Dispatchers.IO) // Changes the context where this flow is executed to Dispatchers.IO
.flatMapLatest { query -> // to avoid the network call results which are not needed more for displaying to the user
getCoinsUseCase(query).catch { emitAll(flowOf(emptyList())}
}
.onEach { coins: List<Coin> -> // go through each list of Coins
coinsList.value = coins
}
.launchIn(viewModelScope)
}
fun searchCoinsList(query: String) {
queryFlow.value = query
}

How to iterate into the collection of class objects in node.js

I have two classes one and two
class One {
constructor(field1, field2) {
this.field1 = field1;
this.field2 = field2;
}
}
module.exports = one;
class Two {
constructor(field11, field22, list) {
this.field11 = field11;
this.field22 = field22;
this.list = list;
}
add(one) {
this.list.push(one);
}
}
module.exports = Two;
Third class imports both classes
const one= require('./one.js');
const two= require('./two.js');
Now, I have a function which creates an object of class two and add some values like,
two = new two();
two.add(new one(1,1000));
two.add(new one(2,2000));
console.log(two.list);
////list is a collection of class one object
Till this point is working fine, I am getting collection
My query is how to iterate through collection
like, I want to access
two.list[0].field1
// not getting the property field1
Try this:
class One {
constructor(field1, field2) {
this.field1 = field1; this.field2 = field2;
}
}
class Two {
constructor(field11, field22, list = []) {
this.field11 = field11; this.field22 = field22;
this.list = list
}
add(one) {
this.list.push(one);
}
}
two = new Two();
two.add(new One(1, 1000));
two.add(new One(2, 2000));
console.log(two.list);
There are some issues in code:
Naming and bracket is not closing correct
Default list parameter is also written in wrong format
class One {
constructor(field1, field2) {
this.field1 = field1;
this.field2 = field2;
}
}
class Two {
constructor(field11, field22, list = []) {
this.field11 = field11;
this.field22 = field22;
this.list = list;
}
add(one) {
this.list.push(one);
}
}
two = new Two();
two.add(new One(1,1000));
two.add(new One(2,2000));
console.log(two.list[0].field1);
Updated your code. Try running it

Kotlin how to combine different receivers via with clause without repeating it

I have Parent-Search-Child system as below:
class Room
class Building {
fun find(by: By) = Room()
}
sealed class By {
abstract fun search(): Room
class ById(id: String) : By() {
override fun search(): Room = Room() // epic search method
}
class ByName(name: String) : By() {
override fun search(): Room = Room() // epic search method
}
class Byurpose(purpose: String) : By() {
override fun search(): Room = Room() // epic search method
}
companion object {
fun id(id: String) = ById(id)
fun name(name: String) = ByName(name)
fun purpose(purpose: String) = Byurpose(purpose)
}
}
Which can be used as follows:
val building = Building()
val room = building.find(By.name("Toilet"))
However, I am not very satisfied with the current syntax, which could be much less verbose in Kotlin. In addition, building.find can appear thousands times in the code. I could implement it differently, but actually I don't own Room, Building or By classes, so I can't. Thus this was my approach:
I implemented context class that stores Building reference, and use it internally as source for search methods:
class BuildingContext(private val building: Building) {
fun String.findById() = building.find(By.id(this))
fun String.findByName() = building.find(By.name(this))
fun String.findByPurpose() = building.find(By.purpose(this))
}
It can be used as below:
with(BuildingContext(building)) {
val room2 = "Toilet".findByName()
}
After that I noticed that I only use one search method in 99% cases, so (for sake of even shorter syntax!) I implemented following classes:
object AlwaysSearchById {
fun String.find(building: Building) = building.find(By.id(this))
}
object AlwaysSearchByName {
fun String.find(building: Building) = building.find(By.name(this))
}
object AlwaysSearchByPurpose {
fun String.find(building: Building) = building.find(By.purpose(this))
}
Which can be used this way:
with(AlwaysSearchByName) {
val room3 = "Toilet".find(building)
}
Unfortunately, building reference appears again. The ideal syntax would be "Toilet".find(). I could fix it re-implementing Always~ classes as follows:
class AlwaysSearchByNameV2(private val building: Building) {
fun String.find() = building.find(By.name(this))
}
And it would be used as below:
with(AlwaysSearchByNameV2(building)) {
val room = "Toilet".find()
}
But it some cases, I would like to access BuildingContext methods as well, so So I have to write:
with(BuildingContext(building)) {
with(AlwaysSearchByNameV2(building)) {
val toilet = "Toilet".find()
val randomRoom = "123".findById()
}
}
The question is - How I reduce multiple with clauses in this case?
In the example above there are only 2 with clauses, but it's only basic example. In real world there could be dozens of them, and writing with(with(with(with(with... would surely be a pain.
On the side note this doesn't work:
with(BuildingContext(building), AlwaysSearchByNameV2(building)) {
val toilet = "Toilet".find()
val randomRoom = "123".findById()
}
nor this
with(*arrayOf(BuildingContext(building), BuildingContext(building))) {
val toilet = "Toilet".find()
val randomRoom = "123".findById()
}
You can write custom scoping functions instead of relying on with all the time. For example you can add an extension function that will run a block of code in scope of AlwaysSearchByNameV2 object:
inline fun BuildingContext.byName(f : AlwaysSearchByNameV2.() -> Unit) = AlwaysSearchByNameV2(building).apply(f)
And use it:
with(BuildingContext(building)) { // this: BuildingContext
byName { // this: AlwaysSearchByNameV2
val toilet = "Toilet".find()
val randomRoom = "123".findById() // can still refer to BuildingContext
}
// back to this: BuildingContext
}

Is it possible to mock accessors by Mockito in Kotlin?

Is it possible to mock getter and setter of the property by Mockito? Something like this:
#Test
fun three() {
val m = mock<Ddd>() {
// on { getQq() }.doReturn("mocked!")
}
assertEquals("mocked!", m.qq)
}
open class Ddd {
var qq : String = "start"
set(value) {
field = value + " by setter"
}
get() {
return field + " by getter"
}
}
To mock getter just write:
val m = mock<Ddd>()
`when`(m.qq).thenReturn("42")
also i suggest to use mockito-kotlin, to use useful extensions and functions like whenever:
val m = mock<Ddd>()
whenever(m.qq).thenReturn("42")
Complementing IRus' answer, you could also use the following syntax:
val mockedObj = mock<SomeClass> {
on { funA() } doReturn "valA"
on { funB() } doReturn "valB"
}
or
val mockedObj = mock<SomeClass> {
on(it.funA()).thenReturn("valA")
on(it.funB()).thenReturn("valB")
}

Resources