Как настроить ответ asyn c Rx метода с Mockito и Kotlin для модульного тестирования - PullRequest
0 голосов
/ 12 января 2020

Я пытаюсь протестировать метод внутри ViewModel, который опирается на случай использования Rx Android. Проблема связана с архитектурой, которую я использую, я могу напрямую протестировать сценарий использования, но у меня возникают проблемы с методом из ViewModel, так как мне нужно иметь возможность настроить ответ asyn c, а затем убедитесь, что некоторые взаимодействия с шиной событий происходят после завершения операции.

Вот мой базовый класс варианта использования:

abstract class BaseUseCase {

    @Inject
    lateinit var scheduler: IScheduler

    private var subscription: Disposable? = Disposables.empty()

    fun <T : Any> execute(
        single: Single<T>,
        onSuccess: Consumer<T>,
        onError: Consumer<in Throwable>
    ) {
        subscription = single
            .compose(scheduler.applySingleDefaultSchedulers())
            .subscribe(onSuccess, onError)
    }

    fun execute(
        completable: Completable,
        onSuccess: Action,
        onError: Consumer<Throwable>
    ) {
        completable.let {
            subscription = completable
                .compose(scheduler.applyCompletableDefaultSchedulers())
                .subscribe(onSuccess, onError)
        }
    }
}

Мой модифицированный Api: interface ItemsApi {

    @POST("/endpoint/items/search/")
    @ErrorType(BaseResponse::class)
    fun searchItems(@Body request: ItemsSearchRequest):
            Single<BaseResponse<ItemsSearchResponse>>
}

Вот пример использования, который использует ViewModel:

class ItemsUseCase @Inject constructor(private val itemsApi: ItemsApi) : BaseUseCase() {

    fun searchItems(request: ItemSearchRequest) =
        itemsApi.searchItems(request)
}

Теперь метод внутри viewModel, который я хочу проверить:

class ItemsListViewModel @Inject constructor(
    private var itemsUseCase: ItemsUseCase,
    private val bus: Bus
) {

    fun searchItems(filter: String) {
        itemsUseCase.execute(
            itemsUseCase.searchItems(ItemsSearchRequest(filter)),
            Consumer { response ->
                bus.post(ItemsSuccessEvent(response.content.items))
            },
            Consumer { error ->
                bus.post(ItemsErrorEvent(error))
            }
        )
    }
}

Наконец, тест я не в состоянии закончить sh:

@RunWith(MockitoJUnitRunner::class)
class NotificationsListViewModelTest {

    @Mock
    lateinit var itemsUseCaseMock: ItemsUseCase
    @Mock
    lateinit var busMock: Bus
    // System under test
    private lateinit var sut: ItemsListViewModel

    @Rule
    @JvmField
    var rule: TestRule = InstantTaskExecutorRule()

    @Before
    fun setUp() {
        sut = ItemsListViewModel(itemsUseCaseMock, busMock)
    }

    @Test
    fun searchItemsSuccess() {
        val filter = "filter"
        val itemsList = mutableListOf<Item>().apply {
            add(Item("default"))
        }
        val request = ItemsSearchRequest(filter)
        val response = BaseResponse(ItemsSearchResponse(itemsList))

        // Setup async response
        doAnswer {
            // In Java I usully do the following:
            // Get the callback from the invocation arguments (in this case "it.arguments[0]" for example)
            // Call "onSuccess" or "onFailure" from the callback with the desired response
        }.whenever(itemsUseCaseMock).searchItems(request)

        // Execute the method I'm testing
        sut.searchItems(searchCriteria)

        // Verify that the event was posted with the proper data
        argumentCaptor<ItemsSuccessEvent>().apply {
            verify(bus).publish(capture())
            assertEquals(itemsList, allValues[0].items)
        }
    }
}

Есть идеи? Заранее спасибо!

...