Тема: Компонентные тесты: как сделать жизнь вашего QA немного проще?
В докладе речь пойдёт о компонентных тестах, в том числе я поделюсь лучшими практиками, которые выработала наша команда, и расскажу, как они помогают нам делать более качественный продукт.
В частности поговорим о том:
– что такое компонентный тест? В чем отличия между юнит-, компонентным и функциональным тестом?
– для чего хороши компонентные тесты и какие проблемы они помогают нам решать?
– как минимизировать стоимость поддержки компонентных тестов без экономии на их надежности.
30. Strictly ConfidentialStrictly Confidential 30
Все ли так радужно?
@Test
fun button___savesEnabledState___afterRotation() {
givenBothFieldsHaveData()
toggleOrientation()
button.check(matches(isEnabled()))
}
31. Strictly ConfidentialStrictly Confidential 31
Все ли так радужно?
@Test
fun button___savesEnabledState___afterRotation() {
givenBothFieldsHaveData()
toggleOrientation()
button.check(matches(isEnabled()))
}
32. Strictly ConfidentialStrictly Confidential 32
Что может скрывать за собой обычный ввод
текста?
fun ViewInteraction.typeText(text: String) {
perform(TypeTextAction(text))
}
33. Strictly ConfidentialStrictly Confidential 33
Что может скрывать за собой обычный ввод
текста?
fun ViewInteraction.typeText(text: String) {
perform(TypeTextAction(text))
}
NoMatchingViewException
34. Strictly ConfidentialStrictly Confidential 34
Что может скрывать за собой обычный ввод
текста?
fun ViewInteraction.typeText(text: String) {
perform(
TypeTextAction(text),
ViewActions.closeSoftKeyboard()
)
}
35. Strictly ConfidentialStrictly Confidential 35
Что может скрывать за собой обычный ввод
текста?
fun ViewInteraction.typeText(text: String) {
perform(
ViewActions.scrollTo(),
TypeTextAction(text),
ViewActions.closeSoftKeyboard()
)
}
36. Strictly ConfidentialStrictly Confidential 36
Что может скрывать за собой обычный ввод
текста?
fun ViewInteraction.typeText(text: String){
Waiter.wait {
perform(
ViewActions.scrollTo(),
TypeTextAction(text),
ViewActions.closeSoftKeyboard()
)
}
}
37. Strictly ConfidentialStrictly Confidential 37
Постепенно элементы становятся сложнее, а
код менее читабельным
Espresso.onView(
Matchers.allOf(
withParent(withId(R.id.email)),
withId(R.id.input)
)
)
)
38. Strictly ConfidentialStrictly Confidential 38
PageObject
“The basic rule of thumb for a page object is that it should allow a
software client to do anything and see anything that a human can.
…
The page object should encapsulate the mechanics required to find
and manipulate the data in the GUI control itself.”
- Martin Fowler
39. Strictly ConfidentialStrictly Confidential 39
class LoginViaMail : PageObject() {
val emailField = InputViewField(withId(R.id.email))
val passwordField = InputViewField(withId(R.id.password))
val logInButton = PageObjectElement(withId(R.id.login))
}
PageObject
40. Strictly ConfidentialStrictly Confidential 40
PageObject
class LoginViaMail : PageObject() {
val emailField = InputViewField(withId(R.id.email))
val passwordField = InputViewField(withId(R.id.password))
val logInButton = PageObjectElement(withId(R.id.login))
fun givenBothFieldsHaveData() {
emailField.write(randomString())
passwordField.write(randomString())
}
}
43. Strictly ConfidentialStrictly Confidential 43
Что получаем?
@Test
fun header___displayedWithTitle___headerLoaded() {
Api.stub {
getHeader.success {
name = “header name"
}
}
screenRule.start()
screen.header.checks.displayedWithText(“header name")
}
44. Strictly ConfidentialStrictly Confidential 44
Проверка должна выглядеть так, как видит ее
пользователь
@Test
fun signUpButton___opensRegistration() {
loginScreen.signUpButton.click()
Screen.ui.checks.actionBarWithTitle("Регистрация")
}
45. Strictly ConfidentialStrictly Confidential 45
Детали проверки должны быть
инкапсулированы
fun assertNotification(notificationMessage: String,
check: Checks.() -> Unit) {
val notification = PageObjectElement(
allOf(
withClassName(containsString("FrameLayout")),
withChild(
allOf(
withId(R.id.info_text),
withText(notificationMessage)
)
)
)
)
check.invoke(notification.checks)
}
47. Strictly ConfidentialStrictly Confidential 47
Работу с глобальным состоянием приложения
уносим в Rule
abstract class SimpleRule : TestRule {
protected open fun before() {
//подготавлиеваем моки
}
protected open fun after() {
//сбрасываем состояние
}
}