bugfix> android > 投稿

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

私の現在の問題は、関数 showNumber(AppCompatTextView, Int) をテストしているときですMockitoで単体テスト、テストに合格するためだけにビューをモックする必要があります(nullにできないため)。

ここで単体テストを行うためのより良いアプローチはどれですか?

私の考えは次のとおりです。

  1. numberTV: AppCompatTextView をつかむ MainActivityPresenter から 、 mainActivityPresenter.getBindViews().mainIV など
  2. テキストロジックのみを返す( "Not compatible"number.toString() または "9+" )プレゼンターから、ただし、要件はビュー(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 件
  • プレゼンターにインターフェイスビューがあるのは正常です。あなたの場合、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レイヤーには、プレゼンターへのコールバックがあるネットワークコールが表示されます。

  • プレゼンターに意見を伝えるべきではないと思います。プレゼンターは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)
    }
    
    

あなたの答え