I am new to mockk framework and trying to write a test case for below function
fun fromCityWeather(cityWeather: List<CityWeather>): HomeWeather {
return HomeWeather(
cityWeather.first().temperature,
cityWeather.first().description
)
}
CityWeather and HomeWeather are my 2 classes and both have temperature and weather as fields. This function is written inside companion object of another class.
Please help me to understand the logic to start with as I have tried multiple times writing the test case while referring the blogs on internet and none worked.
You don't need mockk:
#Test
fun testFromCityWeather() {
val weatherList = listOf(CityWeather("30", "degrees"), CityWeather("12", "degrees"))
val expected = HomeWeather("30", "degrees")
assertEquals(expected, fromCityWeather(weatherList))
}
(Assuming temperature and description are strings)
Related
Am learning android kotlin follow this:
https://developer.android.com/topic/libraries/architecture/viewmodel#kotlin
class MyViewModel : ViewModel() {
private val users: MutableLiveData<List<User>> by lazy {
MutableLiveData<List<User>>().also {
loadUsers(it)
}
}
fun getUsers(): LiveData<List<User>> {
return users
}
private fun loadUsers() {
// Do an asynchronous operation to fetch users.
}
}
Dont know how to write the fun loadUsers()
Here is my User:
class User {
constructor(name: String?) {
this.name = name
}
var name:String? = null
}
If dont use the keyword 'also' , i know how to do it.
But if use 'also' , it seems not work.
Here is how i try to write the fun loadUsers:
private fun loadUsers( it: MutableLiveData<List<User>>){
val users: MutableList<User> = ArrayList()
for (i in 0..9) {
users.add(User("name$i"))
}
it = MutableLiveData<List<User>>(users)
}
Error tips near it : Val cant be ressigned
Part 1: According to the Kotlin documentation, also provides the object in question to the function block as a this parameter. So, every function call and property object you access is implied to refer to your MutableLiveData<List<User>>() object. also returns this from the function block when you are done.
Thus, another way of writing your MutableLiveData<> would be like this:
val users = MutableLiveData<List<User>>()
users.loadUsers()
Part 2: As far as how to implement loadUsers(), that is a separate issue (your question is not clear). You can use Retrofit + RxJava to load the data asynchronously, and that operation is totally outside of the realm of ViewModel or also.
Part 3: With your approach, you have conflicting things going on. Instead of doing a loadUsers() from your lazy {} operation, I would remove your lazy {} operation and create a MutableLiveData<> directly. Then, you can load users later on and update the users property any time new data is loaded. Here is a similar example I worked on a while ago. It uses state flows, but the idea is similar. Also use a data class to model the User instead of a regular class. Another example.
It is solved change to code:
private fun loadUsers( it: MutableLiveData<List<User>>){
val users: MutableList<User> = ArrayList()
for (i in 0..9) {
users.add(User("name$i"))
}
it.value = users
}
it can't be reassigned , but it.value could .
I'm trying to test MVVM with Mockito.
The architecture of MVVM is similar to Android architecture blueprints.
We observe data from repository as LiveData.
And try to test observed value like below code.
class SplashViewModel(
private val appRepository: AppRepository
) {
val appInfo: LiveData<AppInfo> = appRepository.observeAppInfo()
}
#Test
fun getAppInfo() {
`when`(appRepository.observeAppInfo())
.thenReturn(appInfoData)
assertEquals(appInfoData, viewModel.appInfo.getOrAwaitValue())
}
The crucial point is viewModel.appInfo returns null, despite of I used mockito.
The problem
The creation of ViewModel is faster than using mockito.
So appInfo property is initialized with null, cause it dosen't know what observeAppInfo() is.
First solution
At first, I just trying to solve this problem with custom getter. Like this.
class SplashViewModel(
private val appRepository: AppRepository
) {
val appInfo: LiveData<AppInfo> get() = appRepository.observeAppInfo()
}
Now every time I access to appInfo they just re evaluate the data.
But is has it's own problem.
In this situation appVersion is getting error.
val appVersion: LiveData<String> = appInfo.map {
...
}
So every transformation LiveData(Like Transformations, MediatorLiveData) must use custom getter too.
And I felt it's not a great solution.
How do you think of it?
You could use #BeforeClass to make sure your initialisation is run before the tests.
#BeforeClass
fun setup(){
appInfo = appRepository.observeAppInfo()
}
See more
I have created few data classes for my sample application. I need to write unit test cases for those data classes now. I am using Junit4.12. Here is my data class:
data class Tracking(val param1 : String?=null, val param2 : String?=null, val param3 : String?=null){}
I tried writing one basic unit test case for this model like below:
#Test
fun tracking()
{
val trackingData= Tracking("7030054",
"skdfksbfbkjsdf",
"dfkhsbfsjf")
Assert.assertEquals("true",trackingData.param1,"7030054")
}
But i don't see it is right way of performing a unit test case. My Objective for unit test case is to assert an exception if user sends null value as an input. Please help me out.
Your values are all String?, so nullable. If you want them to always be non-null, use String instead. Then you don't need a unit-test since non-nullability will be checked by the compiler.
If they can sometimes be null but not always, you have to first write a function that checks that condition.
A very simple example would be something like
fun simpleValidation() {
if(param1 == null) {
throw RuntimeException("Should not be null")
}
}
in the class tracking.
You can than unit test this by
#Test
fun tracking()
{
val trackingData= Tracking(null,
"skdfksbfbkjsdf",
"dfkhsbfsjf")
Assertions.assertThrows(RuntimeException::class.java) { trackingData.simpleValidation() }
}
assuming you are using JUnit5
I am newbie to writing a test code as an Android developer.
I am using the Kotlin and Retrofit in my android app.
I have a retrofit interface like below:
#GET("/3/movie/{movieId}")
fun getMovie(#Query("api_key") apiKey: String, #Path("movieId") movieId: String): Single<TmdbMovieResp>
The response is "TmdbMovieResp".
And my test code is :
`when`(mockApi.getMovie(mockApiKey, "id")).thenReturn(Single.just(mockMovieResp))
This means I should make the "mockMovieResp".
But the "TmdbMovieResp" has too many member variables.
I can make it, but it's too boring!
And in this case it just one test.
If I have more methods and response types, I should do similar task again and again!
Is there any cool solution?
Here is different approach. You can use https://github.com/andrzejchm/RESTMock
More info by the link. Usage is very simple:
RESTMockServer.whenGET(pathContains("x/y/z/"))
.thenReturnString("your-response-json");
One thing you can do by using your Model classes in your Test method.
As you are using Retrofit you must have some converter like Moshi, GSON for response handling. If you have model classes then use those class for response mocking like below.
val success = TmdbMovieResp() //Response Model class
Then mock the rest api call with the success result like below.
`when`(mockApi.getMovie(mockApiKey, "id")).thenReturn(Single.just(success))
Instead of Mockito, use MockK for your mocking, then utilize relaxed mocks.
class MyClass {
fun foo() : ComplexObject { ... }
}
#Test
fun `test foo`() {
val myClass = spyk(MyClass())
val complex : ComplexObject = mockk(relaxed = true)
every { myClass.foo() } returns complex
assertEquals(complex, myClass.foo())
println(complex.someIntProperty) // prints 1
}
Relaxed mockks return canned data for any value or function, so that you don't need to specify all of those individual properties when they don't matter.
Been trying to duplicate an iOS (Swift) solution in Android (Kotlin). It's fairly straight forward, but I'm not sure if I'm on the right track, or if I need to change up my Kotlin code entirely. (Note this solution is working in Swift)
Here is what's going on in Swift:
enum CarID: String, Codable {
case carHeader
case carView
case dataView
var viewControllerType: UIViewController.Type {
switch self {
case .carView: return CarViewController.self
case .carHeader: return CarHeaderViewController.self
case .dataView: return DataViewController.self
}
}
}
Now my attempt to match it in Kotlin:
enum class ModuleID(val type: String) {
CAR_HEADER("carHeader"),
CAR_VIEW("carView"),
DATA_VIEW("dataView");
val activityType: AppCompatActivity // Wrong return type?
get() = when(this) {
CAR_HEADER -> **CarHeader** // Error here
CAR_VIEW -> CarView
DATA_VIEW -> DataView
}
}
Note I am simply trying to use ActivityViewTypes, just as iOS is using the viewControllerType. The behavior should be near exact.
The error I am seeing reads: "Classifier 'CarHeader' does not have a companion object, and thus must be initialized here."
I attempted to create said companion object, but it just didn't seem necessary, so I backed off. Any suggestions are welcomed! I've also considered looking into a Sealed Class, but not sure if that is necessary here, either.
I have also not addressed the 'Codable - if someone wants to delve into that, that is fine as well.
Thanks!
What you want is a Type, and not an instance, so you'd probably want to return a class, so in Java terms:
UIViewController.Type ==> `Class<? extends Activity>`
With that in mind, following should work:
val activityType: Class<out Activity>
get() = when(this) {
CAR_HEADER -> CarHeader::class.java
CAR_VIEW -> CarView::class.java
DATA_VIEW -> DataView::class.java
}
}
Which you can even use like
startActivity(Intent(this, module.activityType))