java - 重构代码以不抛出 Mockito 的 UnnecessaryStubbingException

标签 java android kotlin mockito rx-java

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

  • 检查是否授予android.permission.READ_CONTACTS
  • 公开联系 Activity
  • 选择联系人并返回应用
  • 再次检查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)和测试:

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

最佳答案

UnnecessaryStubbingException 意味着您正在 stub 某些东西,但并没有真正使用它。这是正确的,在您的情况下,永远不应在测试中调用 interactor.getContact - 这是所需的行为。所以没有必要 stub 它。

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

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

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

关于java - 重构代码以不抛出 Mockito 的 UnnecessaryStubbingException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48029041/

相关文章:

generics - 如何通过泛型从Singleton对象检索值

java - 如何让 Gson 的单转义与 Javascript 的特殊字符和符号的双转义一起使用?

java - Java 字符串格式化

java - 禁用在 ViewPager 中的某些 fragment 上滑动

android - 如何使用 CRONET 在 Android 中发送 “multipart/form-data” POST?

kotlin - 找不到 protoc-gen-grpc-kotlin-1.0.0-windows-x86_64.exe (io.grpc :protoc-gen-grpc-kotlin:1. 0.0)

java - ActiveMQ持久化: some troubles with AMQ Message Store

java - 如何在没有 Math#round 的情况下进行舍入?

android - 将凹凸 sdk 与 Corona sdk 或 trigger.io 一起使用

android - 在 webViewClient 中停止加载