Java 重构代码以避免抛出Mockito';这是不必要的例外

Java 重构代码以避免抛出Mockito';这是不必要的例外,java,android,kotlin,mockito,rx-java,Java,Android,Kotlin,Mockito,Rx Java,我有一个带有按钮的简单视图,该按钮启动意图。操作\u选择作为结果,然后在屏幕上显示所选联系人。为此,必须采取以下步骤: 检查是否授予了android.permission.READ\u CONTACTS 开放式接触活动 选择联系人并返回应用程序 检查android。权限。再次阅读联系人 通过给定uri查找联系人 在屏幕上显示联系人 我想测试一个场景,当一个打开的联系人超过撤销权限,并返回到应用程序与选定的联系人。预期结果不是调用通过其uri查找联系人的方法 不幸的是,当前的实现抛出: org

我有一个带有按钮的简单视图,该按钮启动
意图。操作\u选择
作为结果,然后在屏幕上显示所选联系人。为此,必须采取以下步骤:

  • 检查是否授予了android.permission.READ\u CONTACTS
  • 开放式接触活动
  • 选择联系人并返回应用程序
  • 检查android。权限。再次阅读联系人
  • 通过给定uri查找联系人
  • 在屏幕上显示联系人
  • 我想测试一个场景,当一个打开的联系人超过撤销权限,并返回到应用程序与选定的联系人。预期结果不是调用通过其uri查找联系人的方法

    不幸的是,当前的实现抛出:

    org.mockito.exceptions.misusing.UnnecessaryStubbingException:

    用于:

    我知道我可以用
    Silent
    替换
    StrictStubs
    ,但我正在寻找重构当前代码的更好解决方案

    所有必要的课程和测试:

    class Contact
    
    interface View {
    
        val contactClicks: Observable<Any>
    
        fun setContact(contact: Contact)
    }
    
    interface Interactor {
    
        fun getContact(uri: String): Maybe<Contact>
    }
    
    interface Router {
    
        fun goToContacts(): Maybe<String>
    }
    
    interface Permissioner {
    
        fun requestReadContacts(): Single<Boolean>
    }
    
    class Presenter(
            private val view: View,
            private val interactor: Interactor,
            private val router: Router,
            private val permissioner: Permissioner
    ) {
    
        private val disposables: CompositeDisposable = CompositeDisposable()
    
        fun bindView() {
            view.contactClicks
                    .flatMapSingle { permissioner.requestReadContacts() } //ask first time before opening contacts
                    .filter { it }
                    .flatMapMaybe { router.goToContacts() }
                    .flatMapMaybe {
                        permissioner.requestReadContacts() //ask second time before using ContentResolver
                                .filter { granted -> granted }
                                .flatMap { _ -> interactor.getContact(it) }
                    }
                    .subscribeBy { view.setContact(it) }
                    .addTo(disposables)
        }
    }
    
    @RunWith(MockitoJUnitRunner.StrictStubs::class)
    class PresenterTest {
    
        @Mock
        lateinit var view: View
    
        @Mock
        lateinit var router: Router
    
        @Mock
        lateinit var permissioner: Permissioner
    
        @Mock
        lateinit var interactor: Interactor
    
        @InjectMocks
        lateinit var presenter: Presenter
    
        private val contactClickSubject = PublishSubject.create<Any>()
    
        @Before
        fun setUp() {
            whenever(view.contactClicks).thenReturn(contactClickSubject)
        }
    
        @Test
        fun shouldNotFindContactWhenReturnedWithUriAndPermissionNotGrantedSecondTime() {
            var firstTimeAsk = true
            whenever(permissioner.requestReadContacts()).thenReturn(Single.fromCallable {
                if (firstTimeAsk) {
                    firstTimeAsk = false
                    return@fromCallable true
                } else {
                    return@fromCallable false
                }
            })
            whenever(router.goToContacts()).thenReturn(Maybe.just("contact"))
            var find = false
            whenever(interactor.getContact(any())).thenReturn(Maybe.just(Contact()).doOnSuccess { find = true })
    
            presenter.bindView()
            contactClickSubject.onNext(Any())
    
            assertFalse(find)
        }
    }
    
    class联系人
    界面视图{
    val:可观察到
    乐趣设置联系人(联系人:联系人)
    }
    接口交互器{
    fun-getContact(uri:String):也许吧
    }
    接口路由器{
    有趣的goToContacts():也许吧
    }
    接口许可证{
    fun requestReadContacts():单个
    }
    课堂演示者(
    私有val视图:视图,
    私有val交互器:交互器,
    专用val路由器:,
    私人val许可证持有人:许可证持有人
    ) {
    私有val一次性用品:CompositeDisposable=CompositeDisposable()
    fun bindView(){
    查看联系人点击
    .flatMapSingle{Permission.requestReadContacts()}//在打开联系人之前第一次询问
    .filter{it}
    .flatmap可能{router.goToContacts()}
    A.也许吧{
    permissioner.requestReadContacts()//在使用ContentResolver之前第二次询问
    .filter{已授予->已授予}
    .flatMap{{->interactior.getContact(it)}
    }
    .subscribeBy{view.setContact(it)}
    .addTo(一次性用品)
    }
    }
    @RunWith(MockitoJUnitRunner.StrictStubs::class)
    课堂演示者测试{
    @嘲弄
    lateinit变量视图:视图
    @嘲弄
    lateinit var路由器:路由器
    @嘲弄
    lateinit var许可证持有人:许可证持有人
    @嘲弄
    lateinit变量交互器:交互器
    @注射模拟
    lateinit var演示者:演示者
    private val contactClickSubject=PublishSubject.create()
    @以前
    趣味设置(){
    无论何时(查看。联系人单击)。然后返回(联系人单击主题)
    }
    @试验
    当使用Uri返回且许可证未授予第二次()时,不应找到乐趣{
    var firstTimeAsk=true
    无论何时(permissioner.requestReadContacts())。然后返回(Single.fromCallable{
    如果(首次提问){
    firstTimeAsk=false
    return@fromCallable真的
    }否则{
    return@fromCallable假的
    }
    })
    无论何时(router.goToContacts())。然后返回(可能是.just(“contact”))
    var find=false
    无论何时(interactior.getContact(any()).thenReturn(Maybe.just(Contact()).doOnSuccess{find=true})
    presenter.bindView()文件
    contactClickSubject.onNext(任意())
    assertFalse(查找)
    }
    }
    
    不必要的存根异常
    表示您正在存根某些内容,但实际上并未使用它。这是正确的,在您的情况下,
    interactitor.getContact
    不应该在测试中被调用-这是期望的行为。因此,没有必要对其进行删节

    最简单的解决方案是删除不必要的变量
    var find=false
    并存根-在测试结束时用断言替换它们:

    verify(interactor, never()).getContact(any())
    

    这相当于您当前的解决方案,但比使用帮助器变量更简单。

    不必要的stubbingException
    表示您正在stubbing某些内容,但并未真正使用它。这是正确的,在您的情况下,
    interactitor.getContact
    不应该在测试中被调用-这是期望的行为。因此,没有必要对其进行删节

    最简单的解决方案是删除不必要的变量
    var find=false
    并存根-在测试结束时用断言替换它们:

    verify(interactor, never()).getContact(any())
    
    这相当于您当前的解决方案,但比使用辅助变量更简单