Я работаю над Android какое-то время, но мне впервые приходится писать несколько модульных тестов. У меня есть шаблон проектирования в MVP, поэтому в основном у меня есть Presenter, у которого есть контракт (представление), и он заполнен kotlin, используя сопрограммы.
Вот мой класс Presenter: Repository
и SomeOtherRepository
- это kotlin object
, поэтому он вызывает методы напрямую (идея состоит в том, чтобы не менять способ работы на самом деле)
class Presenter(private val contractView: ContractView) : CoroutinePresenter() {
fun someMethod(param1: Obj1, param2: Obj2) {
launch {
try {
withContext(Dispatchers.IO) {
val data = SomeService.getData() ?: run { throw Exception(ERROR) } // getData() is a suspend function
Repository.doRequest(param1, param2) // doRequest() is a suspend function also
}.let { data ->
if (data == null) {
contractView.onError(ERROR)
} else {
if (SomeOtherRepository.validate(data)) {
contractView.onSuccess()
} else {
contractView.onError(ERROR)
}
}
} catch (exception: Exception) {
contractView.onError(exception)
}
}
}
}
Итак, моя цель - создать модульный тест для этого класса Presenter поэтому я создал следующий класс, чтобы протестировать Presenter
. Вот реализация теста: я прочитал много статей и ссылок на stackoverflow, но все еще имею проблему.
Я настраиваю TestCoroutineRule
, который выглядит следующим образом:
@ExperimentalCoroutinesApi
class TestCoroutineRule(
private val testDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()
) : TestWatcher(), TestCoroutineScope by TestCoroutineScope() {
override fun starting(description: Description?) {
super.starting(description)
Dispatchers.setMain(testDispatcher)
}
override fun finished(description: Description?) {
super.finished(description)
Dispatchers.resetMain()
testDispatcher.cleanupTestCoroutines()
}
private fun TestCoroutineRule.runBlockingTest(block: suspend () -> Unit) =
testDispatcher.runBlockingTest { block() }
}
реализация PresenterTest
:
@ExperimentalCoroutinesApi
class PresenterTest {
@get:Rule
val testCoroutineRule = TestCoroutineRule()
@Mock
private lateinit var view: ContractView
@Mock
private lateinit var repository: Repository
private lateinit var presenter: Presenter
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
presenter = Presenter(view)
}
@Test
fun `test success`() =
testCoroutineRule.runBlockingTest {
// Given
val data = DummyData("test", 0L)
// When
Mockito.`when`(repository.doRequest(param1, param2)).thenReturn(data)
// Then
presenter.someMethod("test", "test")
// Assert / Verify
Mockito.verify(view, Mockito.times(1)).onSuccess()
}
}
Проблема, с которой я столкнулся, заключается в следующей ошибке: Wanted but not invoked: view.onSuccess(); Actually there were zero interactions with this mock
.
ContractView реализован в Activity, поэтому мне было интересно, нужно ли мне использовать Robolectri c, чтобы вызвать метод onSuccess () в контексте Activity. Я также думаю, что у меня может быть проблема с использованием сопрограмм. Я много чего пробовал, но всегда получал эту ошибку в представлении onSuccess et onError, если бы кто-нибудь мог помочь, был бы очень признателен :)