I have a big project for Android with Kotlin using the MVP pattern, and I'm starting to struggle to do Unit Tests (testing the presenters mocking the view interfaces). The reason is I'm passing view references to my functions in the presenters and it's really bad having to mock them, example:
My code would look like:
class MainActivity : Activity(), MainActivityView {
@BindView(R.id.numberTV)
lateinit var numberTV : AppCompatTextView
private val mainActivityPresenter = MainActivityPresenter(this)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mainActivityPresenter.onCreate()
}
override fun showNumber() {
mainActivityPresenter.showNumber(numberTV, 22)
}
}
interface MainActivityView {
fun showNumber()
}
class MainActivityPresenter(private val mainActivityView: MainActivityView) {
fun showNumber(numberTV: AppCompatTextView, number: Int) {
numberTV.text = if (number < 0) {
"Not compatible"
} else if (number < 10) {
number.toString()
} else {
"9+"
}
}
fun onCreate() {
mainActivityView.showNumber()
}
}
My current problem is, when I'm testing the function showNumber(AppCompatTextView, Int)
with Mockito in Unit Tests, I should mock the view just to pass the test (as it can't be null).
Which one would be a better approach to do Unit Tests here?
My thoughts are:
- Grabbing the
numberTV: AppCompatTextView
from theMainActivityPresenter
, such asmainActivityPresenter.getBindViews().mainIV
- Returning just the text logic (
"Not compatible"
,number.toString()
or"9+"
) from the presenter, although sometimes the requirements require to do logic between more than view (2+)
What would you do?
EDIT
I would like to point out that not passing any View
to the presenter, might be an issue with asynchronous calls.
Example: setting an image with Glide:
internal fun showImageAccordingToCache(cachedSplashScreenUri: String?, mainImageView: ImageView) {
Glide.with(context)
/**
* Save in cache, but we say to load it from cache. Otherwise it will throw an error
*/
.setDefaultRequestOptions(defaultDiskStrategy()
.onlyRetrieveFromCache(true))
.load(cachedSplashScreenUri)
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
/**
* And when that error is thrown, we preload the image for the next time.
*/
activityViewPresenter.showLogo()
loadImageInCache(cachedSplashScreenUri)
return true
}
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
return false
}
})
.into(mainImageView)
}