Является ли плохой практикой передача ссылок на представление докладчику в шаблоне MVP? - PullRequest
0 голосов
/ 25 мая 2018

У меня большой проект для Android с Kotlin, использующим шаблон MVP, и я начинаю испытывать трудности с проведением юнит-тестов (тестирование докладчиков, издевающихся над интерфейсами представления).Причина в том, что я передаю ссылки на представления своих функций в докладчиках, и это очень плохо, когда я имитирую их, например:

Мой код будет выглядеть так:

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()
    }
}

Моя текущая проблемато есть, когда я тестирую функцию showNumber(AppCompatTextView, Int) с помощью Mockito в Unit Tests , я должен смоделировать вид только для того, чтобы пройти тест (поскольку он не может быть нулевым).

Какой из них был бы лучшим подходом для проведения юнит-тестов здесь?

Мои мысли:

  1. Получение numberTV: AppCompatTextView из MainActivityPresenter, например mainActivityPresenter.getBindViews().mainIV
  2. Возвращает только текстовую логику ("Not compatible", number.toString() или "9+") от докладчика, хотя иногда требования требуют выполнения логики между больше, чем view (2 +)

Что бы вы сделали?


РЕДАКТИРОВАТЬ

Я хотел бы отметить, что не передавая 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)
}

Ответы [ 2 ]

0 голосов
/ 25 мая 2018

Нормально иметь интерфейс View в вашем докладчике.В вашем случае MainActivityView - это интерфейс, представляющий контракт, которому должна соответствовать Ваша активность.Обычно вы передаете это представление в конструктор презентатора (то, что вы уже делаете), используя кинжал для внедрения его в презентатор.

Теперь это не очень обычно:

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

Теперь докладчик знает о «компонентах 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+");
        }
    }

Чтобы проверить это, вы бы смоделировали представление и увидели, действительно ли в зависимости от числа вызывался каждый из этих методов. (В Java).

@Mock
MainActivityView view;

@Test
public fun shouldShowCorrectNumber() {

    int number = 10;
    presenter.showNumber(number);
    verify(view).showNumber("9+");
}

Что касается асинхронных вызовов, то, что я обычно вижу при использовании библиотеки Glide, это то, что она используется в действии, а не в презентере.Например, другие типы асинхронных вызовов могут передаваться на другие уровни.Обычно я вижу сетевые вызовы на уровне Interactor с обратными вызовами докладчику:

0 голосов
/ 25 мая 2018

Я думаю, что вы не должны передавать взгляды своему докладчику.Ваш докладчик должен вызвать методы mainActivityView для отображения необходимых данных.Это должен быть метод в вашем mainAcitivityView

override fun showNumber(number: String) {
    numberTV.text = number
}

И вы должны вызывать это у вашего докладчика следующим образом:

fun onCreate() {
   showNumber(22)
}

fun showNumber(number: Int) {
    numberString:String = if (number < 0) {
        "Not compatible"
    } else if (number < 10) {
        number.toString()
    } else {
        "9+"
    }
    mainActivityView.showNumber(numberString:String)
}
...