← Назад к вопросам

Какие знаешь способы тестирования Presenter в MVP?

1.8 Middle🔥 162 комментариев
#Архитектура и паттерны#Тестирование

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Тестирование Presenter в архитектуре MVP

В архитектуре MVP (Model-View-Presenter) Presenter выступает центральным компонентом, содержащим бизнес-логику, что делает его ключевым объектом для модульного тестирования. Вот основные подходы и техники:

1. Unit-тесты с моками зависимостей

Наиболее распространённый способ — использование мок-объектов (mock objects) для View и Model. Это позволяет изолировать Presenter и тестировать только его логику.

// Пример теста с Mockito
class UserPresenterTest {
    
    private lateinit var presenter: UserPresenter
    private lateinit var mockView: UserContract.View
    private lateinit var mockRepository: UserRepository
    
    @Before
    fun setUp() {
        mockView = mock()
        mockRepository = mock()
        presenter = UserPresenter(mockView, mockRepository)
    }
    
    @Test
    fun `loadUser should show user data when successful`() {
        // Arrange
        val testUser = User("John", "john@example.com")
        `when`(mockRepository.getUser(anyInt())).thenReturn(testUser)
        
        // Act
        presenter.loadUser(123)
        
        // Assert
        verify(mockView).showLoading()
        verify(mockView).hideLoading()
        verify(mockView).displayUser(testUser)
        verify(mockView, never()).showError(anyString())
    }
    
    @Test
    fun `loadUser should show error when repository fails`() {
        // Arrange
        val errorMessage = "Network error"
        `when`(mockRepository.getUser(anyInt())).thenThrow(IOException(errorMessage))
        
        // Act
        presenter.loadUser(123)
        
        // Assert
        verify(mockView).showLoading()
        verify(mockView).hideLoading()
        verify(mockView).showError(errorMessage)
        verify(mockView, never()).displayUser(any())
    }
}

2. Использование интерфейсов и контрактов

Критически важно, чтобы View и Model были представлены интерфейсами. Это позволяет:

  • Легко создавать мок-реализации для тестов
  • Чётко определять контракты взаимодействия
  • Избегать связывания с конкретными реализациями
// Контракт для четкого определения ответственности
interface UserContract {
    interface View {
        fun showLoading()
        fun hideLoading()
        fun displayUser(user: User)
        fun showError(message: String)
    }
    
    interface Presenter {
        fun loadUser(userId: Int)
        fun onDestroy()
    }
}

3. Тестирование реактивных Presenter (RxJava, Coroutines)

Для Presenter с асинхронными операциями требуются специальные подходы:

Для RxJava:

@Test
fun `rxExample should handle emissions correctly`() {
    // Arrange
    val testObserver = TestObserver<User>()
    `when`(mockRepository.getUserStream()).thenReturn(Observable.just(testUser))
    
    // Act
    presenter.loadUserStream()
        .subscribe(testObserver)
    
    // Assert
    testObserver.assertValue(testUser)
    testObserver.assertComplete()
    verify(mockView).onUserStreamStarted()
}

Для Kotlin Coroutines:

@Test
fun `coroutineExample should update view`() = runTest {
    // Arrange
    val testUser = User("Test", "test@example.com")
    coEvery { mockRepository.getUserSuspend() } returns testUser
    
    // Act
    presenter.loadUserSuspend()
    
    // Assert
    coVerify { mockView.displayUser(testUser) }
}

4. Тестирование жизненного цикла

Presenter часто управляет жизненным циклом, поэтому важно тестировать:

  • Привязку и отвязку от View
  • Очистку ресурсов в onDestroy()
  • Обработку конфигурационных изменений
@Test
fun `onDestroy should clear disposables`() {
    // Arrange
    val compositeDisposable = spyk(CompositeDisposable())
    presenter = UserPresenter(mockView, mockRepository, compositeDisposable)
    
    // Act
    presenter.onDestroy()
    
    // Assert
    verify(compositeDisposable).clear()
    assertTrue(compositeDisposable.isDisposed)
}

5. Интеграционное тестирование Presenter

Хотя модульные тесты преобладают, иногда полезно интеграционное тестирование:

class UserPresenterIntegrationTest {
    
    @Test
    fun `presenter with real repository stub`() {
        // Использование реальной модели с заглушками данных
        val realRepository = UserRepository(
            apiService = createMockApiService(),
            database = createInMemoryDatabase()
        )
        val mockView = mock<UserContract.View>()
        val presenter = UserPresenter(mockView, realRepository)
        
        // Тестирование полного потока
        presenter.loadUser(1)
        
        // Проверка взаимодействия
        verify(mockView).displayUser(any())
    }
}

6. Использование тестовых правил (Test Rules)

Для упрощения настройки тестовой среды:

@RunWith(MockitoJUnitRunner::class)
class UserPresenterTest {
    
    @Mock lateinit var view: UserContract.View
    @Mock lateinit var repository: UserRepository
    @InjectMocks lateinit var presenter: UserPresenter
    
    @Rule @JvmField
    val schedulerRule = RxImmediateSchedulerRule() // Правило для RxJava
    
    @Test
    fun `test with annotations and rules`() {
        // Тест получает автоматически инициализированные зависимости
    }
}

Ключевые принципы успешного тестирования Presenter:

  • Изоляция бизнес-логики от Android-компонентов
  • Полное покрытие сценариев: успешные и ошибочные кейсы
  • Тестирование асинхронных операций с имитацией задержек
  • Проверка всех взаимодействий с View и Model
  • Использование соглашения Given-When-Then для структурирования тестов
  • Минимизация зависимостей от фреймворков Android

Правильно протестированный Presenter обеспечивает стабильность бизнес-логики, упрощает рефакторинг и позволяет безопасно вносить изменения, так как тесты быстро выявят проблемы в логике обработки данных и взаимодействии с View. Современные библиотеки вроде Mockito, JUnit, Espresso (для интеграционных тестов) и специализированные инструменты для реактивного программирования делают этот процесс эффективным и надёжным.

Какие знаешь способы тестирования Presenter в MVP? | PrepBro