在 MVP 模式中将 View 的引用传递给 Presenter 是一种不好的做法吗?

Raf*_*ñoz 5 mvp android unit-testing android-mvp

我有一个使用 MVP 模式的 Kotlin Android 大项目,我开始努力进行单元测试(测试模拟视图界面的演示者)。原因是我在演示者中传递对我的函数的视图引用,并且必须模拟它们真的很糟糕,例如:

我的代码如下所示:

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()
    }
}
Run Code Online (Sandbox Code Playgroud)

我当前的问题是,当我在Unit TestsshowNumber(AppCompatTextView, Int)中使用 Mockito测试函数时,我应该模拟视图以通过测试(因为它不能为空)。

哪种方法是在这里进行单元测试的更好方法?

我的想法是:

  1. numberTV: AppCompatTextView从 中抓取MainActivityPresenter,例如mainActivityPresenter.getBindViews().mainIV
  2. 仅从演示者返回文本逻辑("Not compatible"number.toString()或),尽管有时要求需要在多个视图(2+)之间执行逻辑"9+"

你会怎么办?


编辑

我想指出的是,不将任何内容传递View给演示者可能是异步调用的问题。

示例:使用 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)
}
Run Code Online (Sandbox Code Playgroud)

Lev*_*ira 1

Presenter 中有一个界面 View 是很正常的。在您的情况下,MainActivityView 是代表您的 Activity 必须遵守的合同的接口。您通常会使用 dagger 将视图注入演示器的构造函数(您已经在做的事情)中传递该视图。

现在这不是很常见:

 fun showNumber(numberTV: AppCompatTextView, number: Int) {
        numberTV.text = if (number < 0) {
            "Not compatible"
        } else if (number < 10) {
            number.toString()
        } else {
            "9+"
        }
    }
Run Code Online (Sandbox Code Playgroud)

现在演示者知道了“Android SDK 组件”,这可不是什么好事。在这种情况下,你应该做的是:

 fun showNumber(number: Int) {
        if (number < 0) {
            mainActivityView.setNumberText("Not compatible");
        } else if (number < 10) {
            mainActivityView.setNumberText(number.toString());
        } else {
            mainActivityView.setNumberText("9+");
        }
    }
Run Code Online (Sandbox Code Playgroud)

为了测试这一点,您可以模拟视图并查看是否根据编号实际调用了每个方法。(在 java 中)。

@Mock
MainActivityView view;

@Test
public fun shouldShowCorrectNumber() {

    int number = 10;
    presenter.showNumber(number);
    verify(view).showNumber("9+");
}
Run Code Online (Sandbox Code Playgroud)

至于异步调用,我在使用Glide库时通常看到的是它是在activity中使用的,而不是在presenter中使用的。例如,其他类型的异步调用可能会进入其他层。我通常会在交互器层中看到网络调用以及对演示者的回调: