如何使用 mockito 在我的 retrofit2 演示器上实施单元测试?
How to Implement Unit test on my retrofit2 presenter using mockito?
我想通过对我的代码使用 mockito 依赖项来进行单元测试。它总是失败,因为 view.processMatchData(data) 和 view.hideLoading() 在此演示代码中处于闭包部分,因此单元测试不会检测到它们。请有人帮我解决这个问题。
open class MatchSearchPresenter(
private val view: MatchSearchView,
private val apiService: ApiService,
private val cari : String
) {
fun searchMatch() {
view.showLoading()
apiService.loadSearchMatch(cari).enqueue(object : Callback<MatchSearchResponseModel> {
override fun onResponse(call: Call<MatchSearchResponseModel>, response: Response<MatchSearchResponseModel>)
{
if (response.isSuccessful) {
val data = response.body()!!
view.processMatchData(data)
}
view.hideLoading()
}
override fun onFailure(call: Call<MatchSearchResponseModel>, error: Throwable)
{
Log.e("Error", error.message)
view.hideLoading()
}
})
}
}
这是我的单元测试:
class MatchSearchPresenterTest {
@Mock
private lateinit var view: MatchSearchView
@Mock
private lateinit var apiService: ApiService
@Mock
private lateinit var teamPresenter: MatchSearchPresenter
@Mock
private lateinit var call: Call<MatchSearchResponseModel>
@Mock
private lateinit var something: Callback<MatchSearchResponseModel>
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
val kata = "Man United"
teamPresenter = MatchSearchPresenter(view, apiService, kata )
}
@Test
fun searchMatch() {
val teamId = "Man United"
val teams: MutableList<PrevMatchData> = mutableListOf()
val data = MatchSearchResponseModel(teams)
teamPresenter.searchMatch()
argumentCaptor<MatchSearchView>().apply {
Mockito.verify(apiService.loadSearchMatch(teamId).enqueue(something))
firstValue.processMatchData(data)
firstValue.hideLoading()
}
Mockito.verify(view).showLoading()
Mockito.verify(view).processMatchData(data)
Mockito.verify(view).hideLoading()
}
}
但是显示这样的消息是行不通的:
java.lang.NullPointerException
at com.example.footballleaguecataloguefourth.ui_bottom_navigation.schedule.match_search.MatchSearchPresenter.searchMatch(MatchSearchPresenter.kt:19)
at com.example.footballleaguecataloguefourth.ui_bottom_navigation.schedule.match_search.MatchSearchPresenterTest.searchMatch(MatchSearchPresenterTest.kt:41)
我想你在这里想要的是让 ApiService
调用 return 的调用在排队后立即调用正确的方法。
为此,您可以使用 Mockito 的 thenAnswer
- Here's 示例。在你的情况下,你可以试试这个:
Mockito.`when`(call.enqueue(Mockito.any())).thenAnswer {
(it.getArgument(0) as Callback<MatchSearchResponseModel>).onResponse(
call,
Response.success(MatchSearchResponseModel(/*whatever is needed to build this object*/))
)
}
在这里,您要确保一旦使用任何参数调用 call.enqueue
,它将立即调用 onResponse
方法并返回成功响应。您可以对错误执行类似的操作,也可以调用 onFailure
.
您需要做的最后一件事是确保您的 api 服务 return 是配置的模拟 call
:
Mockito.`when`(apiService.loadSearchMatch(kata)).thenReturn(call)
我会把这个放在每个测试中。所以在你调用你的演示者方法之前,我会像这样配置模拟。
现在,调用 teamPresenter.searchMatch()
,应该调用 apiService.loadSearchMatch(cari)
,这将 return 一旦入队的模拟调用将调用传递的回调的 onResponse
方法。
最后,您可能已经注意到 when
实际上是用反引号书写的。这是因为它是一个需要转义的 kotlin 关键字。不仅出于这个原因,还有更多原因,您可以考虑使用 Mockito Kotlin,这是一个极好的 kotlin 库,包装了 mockito,让生活变得更轻松。
我想通过对我的代码使用 mockito 依赖项来进行单元测试。它总是失败,因为 view.processMatchData(data) 和 view.hideLoading() 在此演示代码中处于闭包部分,因此单元测试不会检测到它们。请有人帮我解决这个问题。
open class MatchSearchPresenter(
private val view: MatchSearchView,
private val apiService: ApiService,
private val cari : String
) {
fun searchMatch() {
view.showLoading()
apiService.loadSearchMatch(cari).enqueue(object : Callback<MatchSearchResponseModel> {
override fun onResponse(call: Call<MatchSearchResponseModel>, response: Response<MatchSearchResponseModel>)
{
if (response.isSuccessful) {
val data = response.body()!!
view.processMatchData(data)
}
view.hideLoading()
}
override fun onFailure(call: Call<MatchSearchResponseModel>, error: Throwable)
{
Log.e("Error", error.message)
view.hideLoading()
}
})
}
}
这是我的单元测试:
class MatchSearchPresenterTest {
@Mock
private lateinit var view: MatchSearchView
@Mock
private lateinit var apiService: ApiService
@Mock
private lateinit var teamPresenter: MatchSearchPresenter
@Mock
private lateinit var call: Call<MatchSearchResponseModel>
@Mock
private lateinit var something: Callback<MatchSearchResponseModel>
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
val kata = "Man United"
teamPresenter = MatchSearchPresenter(view, apiService, kata )
}
@Test
fun searchMatch() {
val teamId = "Man United"
val teams: MutableList<PrevMatchData> = mutableListOf()
val data = MatchSearchResponseModel(teams)
teamPresenter.searchMatch()
argumentCaptor<MatchSearchView>().apply {
Mockito.verify(apiService.loadSearchMatch(teamId).enqueue(something))
firstValue.processMatchData(data)
firstValue.hideLoading()
}
Mockito.verify(view).showLoading()
Mockito.verify(view).processMatchData(data)
Mockito.verify(view).hideLoading()
}
}
但是显示这样的消息是行不通的:
java.lang.NullPointerException
at com.example.footballleaguecataloguefourth.ui_bottom_navigation.schedule.match_search.MatchSearchPresenter.searchMatch(MatchSearchPresenter.kt:19)
at com.example.footballleaguecataloguefourth.ui_bottom_navigation.schedule.match_search.MatchSearchPresenterTest.searchMatch(MatchSearchPresenterTest.kt:41)
我想你在这里想要的是让 ApiService
调用 return 的调用在排队后立即调用正确的方法。
为此,您可以使用 Mockito 的 thenAnswer
- Here's 示例。在你的情况下,你可以试试这个:
Mockito.`when`(call.enqueue(Mockito.any())).thenAnswer {
(it.getArgument(0) as Callback<MatchSearchResponseModel>).onResponse(
call,
Response.success(MatchSearchResponseModel(/*whatever is needed to build this object*/))
)
}
在这里,您要确保一旦使用任何参数调用 call.enqueue
,它将立即调用 onResponse
方法并返回成功响应。您可以对错误执行类似的操作,也可以调用 onFailure
.
您需要做的最后一件事是确保您的 api 服务 return 是配置的模拟 call
:
Mockito.`when`(apiService.loadSearchMatch(kata)).thenReturn(call)
我会把这个放在每个测试中。所以在你调用你的演示者方法之前,我会像这样配置模拟。
现在,调用 teamPresenter.searchMatch()
,应该调用 apiService.loadSearchMatch(cari)
,这将 return 一旦入队的模拟调用将调用传递的回调的 onResponse
方法。
最后,您可能已经注意到 when
实际上是用反引号书写的。这是因为它是一个需要转义的 kotlin 关键字。不仅出于这个原因,还有更多原因,您可以考虑使用 Mockito Kotlin,这是一个极好的 kotlin 库,包装了 mockito,让生活变得更轻松。