mirror of
https://github.com/hmalik144/Candy_Space_tech_test.git
synced 2025-12-10 03:05:27 +00:00
Unit test for repository added
This commit is contained in:
@@ -42,8 +42,9 @@ dependencies {
|
|||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||||
//mockito
|
//mockito and livedata testing
|
||||||
testImplementation 'org.mockito:mockito-inline:2.13.0'
|
testImplementation 'org.mockito:mockito-inline:2.13.0'
|
||||||
|
implementation 'android.arch.core:core-testing'
|
||||||
|
|
||||||
//Retrofit and GSON
|
//Retrofit and GSON
|
||||||
implementation 'com.squareup.retrofit2:retrofit:2.6.0'
|
implementation 'com.squareup.retrofit2:retrofit:2.6.0'
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme">
|
||||||
<activity android:name=".ui.MainActivity">
|
<activity android:name=".ui.main.MainActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package com.example.h_mal.candyspace.data.api
|
package com.example.h_mal.candyspace.data.api
|
||||||
|
|
||||||
import okhttp3.HttpUrl
|
import com.example.h_mal.candyspace.data.api.model.ApiResponse
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
import retrofit2.converter.gson.GsonConverterFactory
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
@@ -15,17 +14,21 @@ interface ApiClass {
|
|||||||
@GET("users?")
|
@GET("users?")
|
||||||
suspend fun getUsersFromApi(@Query("inname") inname: String): Response<ApiResponse>
|
suspend fun getUsersFromApi(@Query("inname") inname: String): Response<ApiResponse>
|
||||||
|
|
||||||
|
//invoke method creating an invocation of the api call
|
||||||
companion object{
|
companion object{
|
||||||
operator fun invoke(
|
operator fun invoke(
|
||||||
|
//injected @params
|
||||||
networkConnectionInterceptor: NetworkConnectionInterceptor,
|
networkConnectionInterceptor: NetworkConnectionInterceptor,
|
||||||
queryParamsInterceptor: QueryParamsInterceptor
|
queryParamsInterceptor: QueryParamsInterceptor
|
||||||
) : ApiClass{
|
) : ApiClass{
|
||||||
|
|
||||||
|
//okHttpClient with interceptors
|
||||||
val okkHttpclient = OkHttpClient.Builder()
|
val okkHttpclient = OkHttpClient.Builder()
|
||||||
.addNetworkInterceptor(networkConnectionInterceptor)
|
.addNetworkInterceptor(networkConnectionInterceptor)
|
||||||
.addInterceptor(queryParamsInterceptor)
|
.addInterceptor(queryParamsInterceptor)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
//retrofit to be used in @Repository
|
||||||
return Retrofit.Builder()
|
return Retrofit.Builder()
|
||||||
.client(okkHttpclient)
|
.client(okkHttpclient)
|
||||||
.baseUrl("https://api.stackexchange.com/2.2/")
|
.baseUrl("https://api.stackexchange.com/2.2/")
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
package com.example.h_mal.candyspace.data.api
|
package com.example.h_mal.candyspace.data.api.model
|
||||||
|
|
||||||
|
import com.example.h_mal.candyspace.data.api.model.User
|
||||||
|
|
||||||
data class ApiResponse(
|
data class ApiResponse(
|
||||||
val items : List<User>?,
|
val items : List<User>?,
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.example.h_mal.candyspace.data.api
|
package com.example.h_mal.candyspace.data.api.model
|
||||||
|
|
||||||
import com.google.gson.annotations.Expose
|
import com.google.gson.annotations.Expose
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.example.h_mal.candyspace.data.api
|
package com.example.h_mal.candyspace.data.api.model
|
||||||
|
|
||||||
import com.google.gson.annotations.Expose
|
import com.google.gson.annotations.Expose
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
package com.example.h_mal.candyspace.data.repositories
|
package com.example.h_mal.candyspace.data.repositories
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import com.example.h_mal.candyspace.data.api.ApiClass
|
import com.example.h_mal.candyspace.data.api.ApiClass
|
||||||
import com.example.h_mal.candyspace.data.api.ApiResponse
|
import com.example.h_mal.candyspace.data.api.model.ApiResponse
|
||||||
import com.example.h_mal.candyspace.data.api.ResponseUnwrap
|
import com.example.h_mal.candyspace.data.api.ResponseUnwrap
|
||||||
import com.example.h_mal.candyspace.data.api.User
|
|
||||||
|
|
||||||
class Repository(
|
class Repository(
|
||||||
private val api: ApiClass
|
private val api: ApiClass
|
||||||
): ResponseUnwrap() {
|
): ResponseUnwrap() {
|
||||||
|
|
||||||
|
//get api response from retrofit class
|
||||||
|
//then unwrap data object from retrofit response class
|
||||||
suspend fun getUsers(username: String): ApiResponse {
|
suspend fun getUsers(username: String): ApiResponse {
|
||||||
return responseUnwrap { api.getUsersFromApi(username) }
|
return responseUnwrap { api.getUsersFromApi(username) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
package com.example.h_mal.candyspace.ui.main
|
package com.example.h_mal.candyspace.ui.home
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.AdapterView
|
|
||||||
import androidx.databinding.DataBindingUtil
|
import androidx.databinding.DataBindingUtil
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.example.h_mal.candyspace.R
|
import com.example.h_mal.candyspace.R
|
||||||
import com.example.h_mal.candyspace.data.api.User
|
import com.example.h_mal.candyspace.data.api.model.User
|
||||||
import com.example.h_mal.candyspace.databinding.MainFragmentBinding
|
import com.example.h_mal.candyspace.databinding.MainFragmentBinding
|
||||||
import com.example.h_mal.candyspace.ui.MainActivity.Companion.viewModel
|
import com.example.h_mal.candyspace.ui.main.MainActivity.Companion.viewModel
|
||||||
import com.example.h_mal.candyspace.utils.displayToast
|
import com.example.h_mal.candyspace.ui.user.ListItemViewModel
|
||||||
|
import com.example.h_mal.candyspace.ui.user.UserProfileFragment
|
||||||
import com.xwray.groupie.GroupAdapter
|
import com.xwray.groupie.GroupAdapter
|
||||||
import com.xwray.groupie.ViewHolder
|
import com.xwray.groupie.ViewHolder
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* UI for the first screen holding the list, search box
|
* UI for the first screen holding the list, search box
|
||||||
*/
|
*/
|
||||||
class MainFragment : Fragment(){
|
class MainFragment : Fragment(){
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -41,26 +41,37 @@ class MainFragment : Fragment(){
|
|||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
|
//observer the live data of what is retrieved from the API call
|
||||||
viewModel.usersLiveData.observe(viewLifecycleOwner, Observer {
|
viewModel.usersLiveData.observe(viewLifecycleOwner, Observer {
|
||||||
|
//create adapter for viewbinding into the recycler view
|
||||||
val mAdapter = GroupAdapter<ViewHolder>().apply {
|
val mAdapter = GroupAdapter<ViewHolder>().apply {
|
||||||
addAll(it.toUserViewModels())
|
addAll(it.toUserViewModels())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//setup the recyclerview
|
||||||
binding.recyclerView.apply {
|
binding.recyclerView.apply {
|
||||||
layoutManager = LinearLayoutManager(context)
|
layoutManager = LinearLayoutManager(context)
|
||||||
setHasFixedSize(true)
|
setHasFixedSize(true)
|
||||||
adapter = mAdapter
|
adapter = mAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Item click listener for recyclerView
|
||||||
|
*
|
||||||
|
*/
|
||||||
mAdapter.setOnItemClickListener { item, _ ->
|
mAdapter.setOnItemClickListener { item, _ ->
|
||||||
|
//get the position of the item clicked
|
||||||
val i = mAdapter.getAdapterPosition(item)
|
val i = mAdapter.getAdapterPosition(item)
|
||||||
|
//set user in @MainViewModel
|
||||||
|
|
||||||
viewModel.setCurrentUser(it[i])
|
viewModel.setCurrentUser(it[i])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* display [UserProfileFragment]
|
||||||
|
*/
|
||||||
activity!!.supportFragmentManager.beginTransaction()
|
activity!!.supportFragmentManager.beginTransaction()
|
||||||
.replace(R.id.container, UserProfileFragment())
|
.replace(R.id.container,
|
||||||
|
UserProfileFragment()
|
||||||
|
)
|
||||||
.addToBackStack("user")
|
.addToBackStack("user")
|
||||||
.commit()
|
.commit()
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
package com.example.h_mal.candyspace.ui.main
|
package com.example.h_mal.candyspace.ui.main
|
||||||
|
|
||||||
|
/**
|
||||||
|
* completion listener for [MainViewModel] when handling async calls
|
||||||
|
*/
|
||||||
interface CompletionListener {
|
interface CompletionListener {
|
||||||
fun onStarted()
|
fun onStarted()
|
||||||
fun onSuccess()
|
fun onSuccess()
|
||||||
|
|||||||
@@ -1,24 +1,22 @@
|
|||||||
package com.example.h_mal.candyspace.ui
|
package com.example.h_mal.candyspace.ui.main
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import com.example.h_mal.candyspace.R
|
import com.example.h_mal.candyspace.R
|
||||||
import com.example.h_mal.candyspace.ui.main.CompletionListener
|
import com.example.h_mal.candyspace.ui.home.MainFragment
|
||||||
import com.example.h_mal.candyspace.ui.main.MainFragment
|
|
||||||
import com.example.h_mal.candyspace.ui.main.MainViewModel
|
|
||||||
import com.example.h_mal.candyspace.ui.main.MainViewModelFactory
|
|
||||||
import com.example.h_mal.candyspace.utils.displayToast
|
import com.example.h_mal.candyspace.utils.displayToast
|
||||||
import com.example.h_mal.candyspace.utils.hide
|
import com.example.h_mal.candyspace.utils.hide
|
||||||
import com.example.h_mal.candyspace.utils.show
|
import com.example.h_mal.candyspace.utils.show
|
||||||
import kotlinx.android.synthetic.main.main_activity.*
|
import kotlinx.android.synthetic.main.main_activity.*
|
||||||
import org.kodein.di.Kodein
|
|
||||||
import org.kodein.di.KodeinAware
|
import org.kodein.di.KodeinAware
|
||||||
import org.kodein.di.android.kodein
|
import org.kodein.di.android.kodein
|
||||||
import org.kodein.di.generic.instance
|
import org.kodein.di.generic.instance
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [MainActivity] hosting the fragments and controlling a lot of the UI
|
||||||
|
*/
|
||||||
class MainActivity : AppCompatActivity(), KodeinAware, CompletionListener {
|
class MainActivity : AppCompatActivity(), KodeinAware, CompletionListener {
|
||||||
|
|
||||||
//retrieve the viewmodel factory from the kodein dependency injection
|
//retrieve the viewmodel factory from the kodein dependency injection
|
||||||
@@ -35,6 +33,7 @@ class MainActivity : AppCompatActivity(), KodeinAware, CompletionListener {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.main_activity)
|
setContentView(R.layout.main_activity)
|
||||||
|
|
||||||
|
//setup home button for back navigation
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true);
|
supportActionBar?.setDisplayHomeAsUpEnabled(true);
|
||||||
supportActionBar?.setDisplayShowHomeEnabled(true);
|
supportActionBar?.setDisplayShowHomeEnabled(true);
|
||||||
|
|
||||||
@@ -43,11 +42,15 @@ class MainActivity : AppCompatActivity(), KodeinAware, CompletionListener {
|
|||||||
viewModel.completionListener = this
|
viewModel.completionListener = this
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
|
//display first fragment
|
||||||
supportFragmentManager.beginTransaction()
|
supportFragmentManager.beginTransaction()
|
||||||
.replace(R.id.container, MainFragment())
|
.replace(R.id.container,
|
||||||
|
MainFragment()
|
||||||
|
)
|
||||||
.commit()
|
.commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//fragment change listener to display title based on current fragment
|
||||||
supportFragmentManager.addOnBackStackChangedListener {
|
supportFragmentManager.addOnBackStackChangedListener {
|
||||||
val name = when (supportFragmentManager.fragments[0]::class.java.simpleName) {
|
val name = when (supportFragmentManager.fragments[0]::class.java.simpleName) {
|
||||||
"UserProfileFragment" -> "User"
|
"UserProfileFragment" -> "User"
|
||||||
@@ -57,6 +60,11 @@ class MainActivity : AppCompatActivity(), KodeinAware, CompletionListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Back button over override
|
||||||
|
* - close app if home fragment
|
||||||
|
* - return to previous fragment if User fragment
|
||||||
|
*/
|
||||||
override fun onBackPressed() {
|
override fun onBackPressed() {
|
||||||
if(supportFragmentManager.backStackEntryCount > 0){
|
if(supportFragmentManager.backStackEntryCount > 0){
|
||||||
supportFragmentManager.popBackStack()
|
supportFragmentManager.popBackStack()
|
||||||
@@ -66,6 +74,7 @@ class MainActivity : AppCompatActivity(), KodeinAware, CompletionListener {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//When home button in toolbar is pressed
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
android.R.id.home -> onBackPressed()
|
android.R.id.home -> onBackPressed()
|
||||||
@@ -73,15 +82,21 @@ class MainActivity : AppCompatActivity(), KodeinAware, CompletionListener {
|
|||||||
return super.onOptionsItemSelected(item)
|
return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* completion listener methods
|
||||||
|
*/
|
||||||
override fun onStarted() {
|
override fun onStarted() {
|
||||||
|
//show loading
|
||||||
progress_circular.show()
|
progress_circular.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSuccess() {
|
override fun onSuccess() {
|
||||||
|
//loading completed - hide loading
|
||||||
progress_circular.hide()
|
progress_circular.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(message: String) {
|
override fun onFailure(message: String) {
|
||||||
|
//loading failed - hide loading and display toast of error
|
||||||
progress_circular.hide()
|
progress_circular.hide()
|
||||||
displayToast(message)
|
displayToast(message)
|
||||||
}
|
}
|
||||||
@@ -3,15 +3,10 @@ package com.example.h_mal.candyspace.ui.main
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.ImageView
|
|
||||||
import androidx.core.content.ContextCompat.getSystemService
|
|
||||||
import androidx.databinding.BindingAdapter
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.example.h_mal.candyspace.R
|
import com.example.h_mal.candyspace.data.api.model.User
|
||||||
import com.example.h_mal.candyspace.data.api.User
|
|
||||||
import com.example.h_mal.candyspace.data.repositories.Repository
|
import com.example.h_mal.candyspace.data.repositories.Repository
|
||||||
import com.squareup.picasso.Picasso
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -21,29 +16,39 @@ class MainViewModel(
|
|||||||
private val repository: Repository
|
private val repository: Repository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
// string binded to the edittext input in @R.layout.main_fragment
|
||||||
var searchString: String? = null
|
var searchString: String? = null
|
||||||
|
|
||||||
var completionListener: CompletionListener? = null
|
var completionListener: CompletionListener? = null
|
||||||
|
|
||||||
|
//data objects - live data and var
|
||||||
val usersLiveData = MutableLiveData<List<User>>()
|
val usersLiveData = MutableLiveData<List<User>>()
|
||||||
var currentUserLiveData: User? = null
|
var currentUserLiveData: User? = null
|
||||||
|
|
||||||
fun submit(view: View){
|
/**
|
||||||
|
* view binding of the submit button in @R.layout.main_fragment
|
||||||
|
*/
|
||||||
|
fun submit(view: View?){
|
||||||
|
//close keyboard when clicked
|
||||||
view.let { v ->
|
view.let { v ->
|
||||||
val imm = view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
|
val imm = view?.context?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
|
||||||
imm?.hideSoftInputFromWindow(v.windowToken, 0)
|
imm?.hideSoftInputFromWindow(v?.windowToken, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
completionListener?.onStarted()
|
completionListener?.onStarted()
|
||||||
|
//return if search string is empty
|
||||||
if (searchString.isNullOrEmpty()){
|
if (searchString.isNullOrEmpty()){
|
||||||
completionListener?.onFailure("Search box is empty")
|
completionListener?.onFailure("Search box is empty")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//open a coroutine on the Main thread and update views upon load
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
try {
|
try {
|
||||||
|
//retrieve response from API call
|
||||||
val apiResponse = repository.getUsers(searchString!!)
|
val apiResponse = repository.getUsers(searchString!!)
|
||||||
|
|
||||||
|
//unwrap list of user out of ApiResponse
|
||||||
apiResponse.items?.let {
|
apiResponse.items?.let {
|
||||||
if (it.isNotEmpty()){
|
if (it.isNotEmpty()){
|
||||||
//update live data
|
//update live data
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import com.example.h_mal.candyspace.data.repositories.Repository
|
import com.example.h_mal.candyspace.data.repositories.Repository
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Viewmodel factory for [MainViewModel]
|
||||||
|
* @repository injected into MainViewModel
|
||||||
|
*/
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
class MainViewModelFactory(
|
class MainViewModelFactory(
|
||||||
private val repository: Repository
|
private val repository: Repository
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.example.h_mal.candyspace.ui.main
|
package com.example.h_mal.candyspace.ui.user
|
||||||
|
|
||||||
import com.example.h_mal.candyspace.R
|
import com.example.h_mal.candyspace.R
|
||||||
import com.example.h_mal.candyspace.data.api.User
|
import com.example.h_mal.candyspace.data.api.model.User
|
||||||
import com.example.h_mal.candyspace.databinding.ListItemLayoutBinding
|
import com.example.h_mal.candyspace.databinding.ListItemLayoutBinding
|
||||||
import com.xwray.groupie.databinding.BindableItem
|
import com.xwray.groupie.databinding.BindableItem
|
||||||
|
|
||||||
@@ -1,15 +1,13 @@
|
|||||||
package com.example.h_mal.candyspace.ui.main
|
package com.example.h_mal.candyspace.ui.user
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.databinding.DataBindingUtil
|
import androidx.databinding.DataBindingUtil
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
|
|
||||||
import com.example.h_mal.candyspace.R
|
import com.example.h_mal.candyspace.R
|
||||||
import com.example.h_mal.candyspace.databinding.FragmentUserProfileBinding
|
import com.example.h_mal.candyspace.databinding.FragmentUserProfileBinding
|
||||||
import com.example.h_mal.candyspace.ui.MainActivity
|
import com.example.h_mal.candyspace.ui.main.MainActivity.Companion.viewModel
|
||||||
import com.example.h_mal.candyspace.ui.MainActivity.Companion.viewModel
|
|
||||||
import com.squareup.picasso.Picasso
|
import com.squareup.picasso.Picasso
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,7 +18,8 @@ import com.squareup.picasso.Picasso
|
|||||||
class UserProfileFragment : Fragment() {
|
class UserProfileFragment : Fragment() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun newInstance() = UserProfileFragment()
|
fun newInstance() =
|
||||||
|
UserProfileFragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
lateinit var binding: FragmentUserProfileBinding
|
lateinit var binding: FragmentUserProfileBinding
|
||||||
@@ -29,13 +28,13 @@ class UserProfileFragment : Fragment() {
|
|||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
activity?.actionBar?.setHomeButtonEnabled(true)
|
// Inflate the layout for this fragment into data binding
|
||||||
// Inflate the layout for this fragment
|
|
||||||
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_user_profile, container, false)
|
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_user_profile, container, false)
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Update the data for viewbinding onResume as data would have changed when selecting a new user
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
viewModel.currentUserLiveData?.let {
|
viewModel.currentUserLiveData?.let {
|
||||||
@@ -2,14 +2,14 @@
|
|||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
tools:context=".ui.main.UserProfileFragment">
|
tools:context=".ui.user.UserProfileFragment">
|
||||||
|
|
||||||
<data>
|
<data>
|
||||||
<import type="com.example.h_mal.candyspace.R"/>
|
<import type="com.example.h_mal.candyspace.R"/>
|
||||||
<import type="com.example.h_mal.candyspace.utils.ConverterUtil"/>
|
<import type="com.example.h_mal.candyspace.utils.ConverterUtil"/>
|
||||||
<variable
|
<variable
|
||||||
name="user"
|
name="user"
|
||||||
type="com.example.h_mal.candyspace.data.api.User" />
|
type="com.example.h_mal.candyspace.data.api.model.User" />
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<data>
|
<data>
|
||||||
<variable
|
<variable
|
||||||
name="user"
|
name="user"
|
||||||
type="com.example.h_mal.candyspace.data.api.User" />
|
type="com.example.h_mal.candyspace.data.api.model.User" />
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
android:id="@+id/container"
|
android:id="@+id/container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".ui.MainActivity" >
|
tools:context=".ui.main.MainActivity" >
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/progress_circular"
|
android:id="@+id/progress_circular"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
android:id="@+id/main"
|
android:id="@+id/main"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".ui.main.MainFragment">
|
tools:context=".ui.home.MainFragment">
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package com.example.h_mal.candyspace.data.repositories
|
package com.example.h_mal.candyspace.data.repositories
|
||||||
|
|
||||||
import com.example.h_mal.candyspace.data.api.ApiClass
|
import com.example.h_mal.candyspace.data.api.ApiClass
|
||||||
import com.example.h_mal.candyspace.data.api.ApiResponse
|
import com.example.h_mal.candyspace.data.api.model.ApiResponse
|
||||||
import com.example.h_mal.candyspace.data.api.User
|
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import okhttp3.MediaType
|
import okhttp3.MediaType
|
||||||
import okhttp3.ResponseBody
|
import okhttp3.ResponseBody
|
||||||
|
|||||||
@@ -1,12 +1,58 @@
|
|||||||
package com.example.h_mal.candyspace.ui.main
|
package com.example.h_mal.candyspace.ui.main
|
||||||
|
|
||||||
|
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||||
|
import com.example.h_mal.candyspace.data.api.ApiClass
|
||||||
|
import com.example.h_mal.candyspace.data.api.model.ApiResponse
|
||||||
|
import com.example.h_mal.candyspace.data.api.model.User
|
||||||
|
import com.example.h_mal.candyspace.data.repositories.Repository
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
|
|
||||||
import org.junit.Assert.*
|
import org.junit.Assert.*
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.rules.TestRule
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito
|
||||||
|
import org.mockito.Mockito.mock
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
import retrofit2.Response
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
class MainViewModelTest {
|
class MainViewModelTest {
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
var rule: TestRule = InstantTaskExecutorRule()
|
||||||
|
|
||||||
|
lateinit var viewModel: MainViewModel
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
lateinit var repository: Repository
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
viewModel = MainViewModel(repository)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getApiFromRepository_SuccessfulReturn() = runBlocking{
|
||||||
|
val user = mock(User::class.java)
|
||||||
|
val mockApiResponse = ApiResponse(listOf(user),null,null,null)
|
||||||
|
|
||||||
|
Mockito.`when`(repository.getUsers("12345")).thenReturn(mockApiResponse)
|
||||||
|
|
||||||
|
viewModel.submit(null)
|
||||||
|
|
||||||
|
assertEquals(mockApiResponse.items, viewModel.usersLiveData.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getApiFromRepository_unsuccessfulReturn() = runBlocking{
|
||||||
|
Mockito.`when`(repository.getUsers("12345")).thenAnswer{ throw IOException() }
|
||||||
|
|
||||||
|
viewModel.submit(null)
|
||||||
|
|
||||||
|
assertEquals(null, viewModel.usersLiveData.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user