【问题标题】:Refactor code to not throw Mockito's UnnecessaryStubbingException重构代码以不抛出 Mockito 的 UnnecessaryStubbingException
【发布时间】:2017-12-29 22:20:58
【问题描述】:

我有一个简单的视图,其按钮以Intent.ACTION_PICK 开头以获得结果,然后在屏幕上显示所选联系人。为此,必须采取以下步骤:

  • 检查是否授予android.permission.READ_CONTACTS
  • 打开联系人活动
  • 选择联系人并返回应用
  • 再次检查android.permission.READ_CONTACTS
  • 通过给定的 uri 查找联系人
  • 在屏幕上显示联系人

我想测试当一个打开联系人而不是撤销权限并返回到选定联系人的应用程序时的场景。预期结果不是调用通过其 uri 查找联系人的方法。

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

org.mockito.exceptions.misusing.UnnecessaryStubbingException:

为:

whenever(interactor.getContact(any())).thenReturn(Maybe.just(Contact()).doOnSuccess { find = true })

我知道我可以用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)
    }
}

【问题讨论】:

    标签: java android kotlin mockito rx-java


    【解决方案1】:

    UnnecessaryStubbingException 表示您正在存根某些东西,但并没有真正使用它。这是正确的,在你的情况下 interactor.getContact 不应该在测试中被调用 - 这是期望的行为。因此,对它进行 stub 是没有意义的。

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

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

    这等效于您当前的解决方案,但比使用辅助变量更直接。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-25
      • 1970-01-01
      相关资源
      最近更新 更多