SlideShare a Scribd company logo
1 of 254
Download to read offline
DON’T DO IT IN ANDROID
TEST AUTOMATION
ALEKSEI TIURIN, VK
WHO I AM?
LEAD AUTOMATION ENGINEER AT VK
•vk.com/alex_tiurin
•github.com/alex-tiurin
•linkedin.com/in/aleksei-tiurin
2
DO YOU REMEMBER ME?
PREVIOUSLY ON HEISENBUG
РЕШАЕМ ПРОБЛЕМЫ  ESPRESSO АВТОТЕСТОВ ANDROID В РЕАЛЬНОМ МИРЕ
4
WHAT ARE WE GONNA TALK ABOUT?
TYPES OF MISTAKES
5
WHAT ARE WE GONNA TALK ABOUT?
TYPES OF MISTAKES
▸ Test data usage
6
WHAT ARE WE GONNA TALK ABOUT?
TYPES OF MISTAKES
▸ Test data usage
▸ Test flow
7
WHAT ARE WE GONNA TALK ABOUT?
TYPES OF MISTAKES
▸ Test data usage
▸ Test flow
▸ Test framework architecture
8
WHAT ARE WE GONNA TALK ABOUT?
TYPES OF MISTAKES
▸ Test data usage
▸ Test flow
▸ Test framework architecture
▸ Reporting
9
MARTY-JUNIOR
MARTY-JUNIOR
DOC-SENIOR
TEST CLASS SKELETON
class SomeTestClass {
...
}
13
TEST CLASS SKELETON
class SomeTestClass {
companion object {
@BeforeClass @JvmStatic
fun executeBeforeAllTests(){}
}
...
}
14
TEST CLASS SKELETON
class SomeTestClass {
companion object {
@BeforeClass @JvmStatic
fun executeBeforeAllTests(){}
}
@Before
fun executeBeforeEachTest(){}
...
}
15
TEST CLASS SKELETON
class SomeTestClass {
companion object {
@BeforeClass @JvmStatic
fun executeBeforeAllTests(){}
}

@Before
fun executeBeforeEachTest(){}
@Test
fun assertSmthTest(){}
}
16
TEST CLASS SKELETON
class SomeTestClass {
companion object {
@BeforeClass @JvmStatic
fun executeBeforeAllTests(){}
}

@get:Rule
val activityRule = ActivityTestRule(MainActivity::class.java)
@Before
fun executeBeforeEachTest(){}
@Test
fun assertSmthTest(){}
}
17
TEST DATA USAGE 18
TEST DATA USAGE
MARTY, STOP USING TEST DATA EVERYWHERE!!!
19
TEST DATA IS USED EVERYWHERE
object TestData {
val chatId = "..."
val documentId = "..."
}
20
TEST DATA IS USED EVERYWHERE
object TestData {
val chatId = "..."
val documentId = "..."
}
// Somewhere in user steps
fun sendDocInChat(user: User){
addDocToUser(user, TestData.documentId)
sendDoc(TestData.chatId,
TestData.documentId)
}
21
TEST DATA IS USED EVERYWHERE
object TestData {
val chatId = "..."
val documentId = "..."
}
// Somewhere in user steps
fun sendDocInChat(user: User){
addDocToUser(user, TestData.documentId)
sendDoc(TestData.chatId,
TestData.documentId)
}
// Somewhere in test class
@Test fun someTest(){
sendDocInChat(user)
}
22
TEST DATA IS USED EVERYWHERE
WHAT’S A PROBLEM
23
TEST DATA IS USED EVERYWHERE
WHAT’S A PROBLEM
▸ Lost a control on test data
24
TEST DATA IS USED EVERYWHERE
WHAT’S A PROBLEM
▸ Lost a control on test data
▸ Unexpected test application condition
25
TEST DATA IS USED EVERYWHERE
WHAT’S A PROBLEM
▸ Lost a control on test data
▸ Unexpected test application condition
▸ The same data can be used by several tests at the same time
26
TEST SUITES
TEST CLASSES
TEST STEPSASSERTIONS
TEST ADAPTERS
WHERE TEST DATA SHOULD BE USED? 27
TEST SUITES
TEST CLASSES
TEST DATA
TEST STEPSASSERTIONS
TEST ADAPTERS
28
PREVIOUSLY ON HEISENBUG
Д. БУЗДИН - КАК ПОСТРОИТЬ СВОЙ ФРЕЙМВОРК ДЛЯ АВТОТЕСТОВ?
29
SOLUTION
▸ Restrict the scope of test data definition to test class / test suite
TEST DATA USAGE 30
TEST DATA IS USED EVERYWHERE
object TestData {
val chatId = "..."
val documentId = "..."
}
// Somewhere in user steps
fun sendDocInChat(user: User){
addDocToUser(user, TestData.documentId)
sendDoc(TestData.chatId,
TestData.documentId)
}
31
RESTRICT THE SCOPE OF TEST DATA DEFINITION TO TEST CLASS / TEST SUITE
object TestData {
val chatId = "..."
val documentId = "..."
}
// Somewhere in user steps
fun sendDocInChat(user: User, docId: String, chatId: String){
addDocToUser(user, docId)
sendDoc(chatId, docId)
}
32
object TestData {
val chatId = "..."
val documentId = "..."
}
// Somewhere in user steps
fun sendDocInChat(user: User, docId: String, chatId: String){
addDocToUser(user, docId)
sendDoc(chatId, docId)
}
33RESTRICT THE SCOPE OF TEST DATA DEFINITION TO TEST CLASS / TEST SUITE
object TestData {
val chatId = "..."
val documentId = "..."
}
// Somewhere in user steps
fun sendDocInChat(user: User, docId: String, chatId: String){
addDocToUser(user, docId)
sendDoc(chatId, docId)
}
// Somewhere in test class
@Test fun someTest(){
sendDocInChat(user, documentId, chatId)
}
34RESTRICT THE SCOPE OF TEST DATA DEFINITION TO TEST CLASS / TEST SUITE
SOLUTION
▸ Restrict the scope of test data definition to test class / test suite
TEST DATA USAGE 35
SOLUTION
▸ Restrict the scope of test data definition to test class / test suite
▸ Store read-only data as project constants
TEST DATA USAGE 36
STORE READ-ONLY DATA AS PROJECT CONSTANTS
object TestData {
// editable value, should be calculated somehow
val chatId = TestDataManager.createChat()
val documentId = "..."
}
// Somewhere in test class
@Test fun someTest(){
sendDocInChat(user, documentId, chatId)
}
37
STORE READ-ONLY DATA AS PROJECT CONSTANTS
object TestData {
// editable value, should be calculated somehow
val chatId = TestDataManager.createChat()
val documentId = TestData.documentId // read-only value
}
// Somewhere in test class
@Test fun someTest(){
sendDocInChat(user, documentId, chatId)
}
38
MISTAKE
TEST DATA IS DEFINED AFTER ACTIVITY LAUNCH
39
MISTAKE
TEST DATA IS DEFINED AFTER ACTIVITY LAUNCH
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java)
@Before
fun setUp(){
Api.addChatMessage(contact.id, "text")
}
40
TEST CLASS METHODS ORDER
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java)
@Before
fun setUp(){
Api.addChatMessage(contact.id, "text")
}
REASON: @BEFORE EXECUTES AFTER ACTIVITY LAUNCHED
41
TEST CLASS METHODS ORDER
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java)
@Before
fun setUp(){
Api.addChatMessage(contact.id, "text")
}
ActivityLaunched
REASON: @BEFORE EXECUTES AFTER ACTIVITY LAUNCHED
42
TEST CLASS METHODS ORDER
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java)
@Before
fun setUp(){
Api.addChatMessage(contact.id, "text")
}
REASON: @BEFORE EXECUTES AFTER ACTIVITY LAUNCHED
@Before
43
ActivityLaunched
TEST CLASS METHODS ORDER
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java)
@Before
fun setUp(){
Api.addChatMessage(contact.id, "text")
}
REASON: @BEFORE EXECUTES AFTER ACTIVITY LAUNCHED
@Test
44
@Before
ActivityLaunched
TEST CLASS METHODS ORDER
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java)
@Before
fun setUp(){
Api.addChatMessage(contact.id, "text")
}
REASON: @BEFORE EXECUTES AFTER ACTIVITY LAUNCHED
45
WHEN TEST DATA SHOULD BE DEFINED 46
WHEN TEST DATA SHOULD BE DEFINED
DEFINE
TEST DATA
47
WHEN TEST DATA SHOULD BE DEFINED
LAUNCH
ACTIVITY
DEFINE
TEST DATA
48
WHEN TEST DATA SHOULD BE DEFINED
LAUNCH
ACTIVITY
DEFINE
TEST DATA @TEST 1
49
WHEN TEST DATA SHOULD BE DEFINED
LAUNCH
ACTIVITY
DEFINE
TEST DATA @TEST 1
@TEST 2
50
TEST CLASS METHODS ORDER
@BeforeClass
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java)
companion object {
@BeforeClass @JvmStatic
fun setUpTestData(){
Api.addChatMessage(contact.id, "text")
}
}
SOLUTION #1: SET UP DATA IN @BEFORECLASS
51
TEST CLASS METHODS ORDER
@BeforeClass
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java)
companion object {
@BeforeClass @JvmStatic
fun setUpTestData(){
Api.addChatMessage(contact.id, "text")
}
}
SOLUTION #1: SET UP DATA IN @BEFORECLASS
52
TEST CLASS METHODS ORDER
@BeforeClass
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java)
companion object {
@BeforeClass @JvmStatic
fun setUpTestData(){
Api.addChatMessage(contact.id, "text")
}
}
SOLUTION #1: SET UP DATA IN @BEFORECLASS
ActivityLaunched
@Test
53
TEST CLASS METHODS ORDER
@BeforeClass
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java)
companion object {
@BeforeClass @JvmStatic
fun setUpTestData(){
Api.addChatMessage(contact.id, "text")
}
}
SOLUTION #1: SET UP DATA IN @BEFORECLASS
ActivityLaunched
@Test
54
TEST CLASS METHODS ORDER
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java,
initialTouchMode = false,
launchActivity = false)
@Before
fun setUp(){
Api.addChatMessage(contact.id, "text")
activityRule.launchActivity(Intent())
}
SOLUTION #2: LAUNCH ACTIVITY = FALSE
55
TEST CLASS METHODS ORDER
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java,
initialTouchMode = false,
launchActivity = false)
@Before
fun setUp(){
Api.addChatMessage(contact.id, "text")
activityRule.launchActivity(Intent())
}
SOLUTION #2: LAUNCH ACTIVITY = FALSE
56
TEST CLASS METHODS ORDER
@BeforeClass
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java,
initialTouchMode = false,
launchActivity = false)
@Before
fun setUp(){
Api.addChatMessage(contact.id, "text")
activityRule.launchActivity(Intent())
}
SOLUTION #2: LAUNCH ACTIVITY = FALSE
57
TEST CLASS METHODS ORDER
@BeforeClass
@Before
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java,
initialTouchMode = false,
launchActivity = false)
@Before
fun setUp(){
Api.addChatMessage(contact.id, "text")
activityRule.launchActivity(Intent())
}
SOLUTION #2: LAUNCH ACTIVITY = FALSE
58
TEST CLASS METHODS ORDER
@BeforeClass
@Before
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java,
initialTouchMode = false,
launchActivity = false)
@Before
fun setUp(){
Api.addChatMessage(contact.id, "text")
activityRule.launchActivity(Intent())
}
SOLUTION #2: LAUNCH ACTIVITY = FALSE
59
TEST CLASS METHODS ORDER
@BeforeClass
@Before
ActivityLaunched
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java,
initialTouchMode = false,
launchActivity = false)
@Before
fun setUp(){
Api.addChatMessage(contact.id, "text")
activityRule.launchActivity(Intent())
}
SOLUTION #2: LAUNCH ACTIVITY = FALSE
60
TEST CLASS METHODS ORDER
@BeforeClass
@Before
ActivityLaunched
@Test
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java,
initialTouchMode = false,
launchActivity = false)
@Before
fun setUp(){
Api.addChatMessage(contact.id, "text")
activityRule.launchActivity(Intent())
}
SOLUTION #2: LAUNCH ACTIVITY = FALSE
61
TEST CLASS METHODS ORDER
SOLUTION #3: CUSTOM SETUP RULE
class SetUpTearDownRule : TestWatcher() {
val setUps = mutableListOf<Condition>()
val tearDowns = mutableListOf<Condition>()
}
62
TEST CLASS METHODS ORDER
SOLUTION #3: CUSTOM SETUP RULE
class SetUpTearDownRule : TestWatcher() {
val setUps = mutableListOf<Condition>()
val tearDowns = mutableListOf<Condition>()
}
63
TEST CLASS METHODS ORDER
SOLUTION #3: CUSTOM SETUP RULE
class SetUpTearDownRule : TestWatcher() {
val setUps = mutableListOf<Condition>()
val tearDowns = mutableListOf<Condition>()
}
64
TEST CLASS METHODS ORDER
class SetUpTearDownRule : TestWatcher() {
private val setUps = mutableListOf<Condition>()
inner class Condition(
val counter: Int,
val key: String,
val actions: () -> Unit
)
}
SOLUTION #3: CUSTOM SETUP RULE
65
TEST CLASS METHODS ORDER
class SomeTestClass {
@get:Rule
val setupRule = SetUpTearDownRule()
.addSetUp {
Api.addChatMessage(contact.id, "text")
}
...
}
SOLUTION #3: CUSTOM SETUP RULE
66
TEST CLASS METHODS ORDER
class SomeTestClass {
@get:Rule
val setupRule = SetUpTearDownRule()
.addSetUp {
Api.addChatMessage(contact.id, "text")
}
...
}
SOLUTION #3: CUSTOM SETUP RULE
67
TEST CLASS METHODS ORDER
class SomeTestClass {
@get:Rule
val setupRule = SetUpTearDownRule()
.addSetUp {
Api.addChatMessage(contact.id, "text")
}
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java)
...
}
@BeforeClassSOLUTION #3: CUSTOM SETUP RULE
68
TEST CLASS METHODS ORDER
class SomeTestClass {
@get:Rule
val setupRule = SetUpTearDownRule()
.addSetUp {
Api.addChatMessage(contact.id, "text")
}
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java)
...
}
@BeforeClass
SetUps.actions
SOLUTION #3: CUSTOM SETUP RULE
69
TEST CLASS METHODS ORDER
class SomeTestClass {
@get:Rule
val setupRule = SetUpTearDownRule()
.addSetUp {
Api.addChatMessage(contact.id, "text")
}
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java)
...
}
@BeforeClass
SetUps.actions
ActivityLaunched
SOLUTION #3: CUSTOM SETUP RULE
70
TEST CLASS METHODS ORDER
class SomeTestClass {
@get:Rule
val setupRule = SetUpTearDownRule()
.addSetUp {
Api.addChatMessage(contact.id, "text")
}
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java)
...
}
@BeforeClass
SetUps.actions
ActivityLaunched
@Before
SOLUTION #3: CUSTOM SETUP RULE
71
TEST CLASS METHODS ORDER
class SomeTestClass {
@get:Rule
val setupRule = SetUpTearDownRule()
.addSetUp {
Api.addChatMessage(contact.id, "text")
}
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java)
...
}
@BeforeClass
SetUps.actions
ActivityLaunched
@Before
@Test
SOLUTION #3: CUSTOM SETUP RULE
72
TEST CLASS METHODS ORDER
class SomeTestClass {
@get:Rule
val setupRule = SetUpTearDownRule()
.addSetUp {
Api.addChatMessage(contact.id, "text")
}
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java)
...
}
SOLUTION #3: CUSTOM SETUP RULE
73
74
SOLUTION #3
DOESN’T WORK
WTF?
TEST CLASS METHODS ORDER
class SomeTestClass {
@get:Rule
val setupRule = SetUpTearDownRule()
.addSetUp {
Api.addChatMessage(contact.id, "text")
}
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java)
...
}
SOLUTION #3 DOESN’T WORK ALWAYS! WHY?
75
TEST CLASS METHODS ORDER
class SomeTestClass {
@get:Rule
val setupRule = SetUpTearDownRule()
.addSetUp {
Api.addChatMessage(contact.id, "text")
}
@get:Rule
val activityRule =
ActivityTestRule(MainActivity::class.java)
...
}
SOLUTION #3 DOESN’T WORK ALWAYS! WHY?
76
TEST CLASS METHODS ORDER
@get:Rule
val setupRule = SetUpTearDownRule().addSetUp{...}
@get:Rule
val activityRule = ActivityTestRule(MainActivity::class.java)
@get:Rule
val ruleChain = RuleChain.outerRule(setupRule)
.around(activityRule)
ORDER RULES WITH RULECHAIN
77
TEST CLASS METHODS ORDER
@get:Rule
val setupRule = SetUpTearDownRule().addSetUp{...}
@get:Rule
val activityRule = ActivityTestRule(MainActivity::class.java)
@get:Rule
val ruleChain = RuleChain.outerRule(setupRule)
.around(activityRule)
ORDER RULES WITH RULECHAIN
78
TEST CLASS METHODS ORDER
val setupRule = SetUpTearDownRule().addSetUp{...}
val activityRule = ActivityTestRule(MainActivity::class.java)
@get:Rule
val ruleChain = RuleChain.outerRule(setupRule)
.around(activityRule)
ORDER RULES WITH RULECHAIN
79
80
MARTY, ARE YOU REALLY
TELLING ME YOUR PLAN
IS BASED ON
“BACK TO THE FUTURE”?
81
MARTY, ARE YOU REALLY
TELLING ME YOUR PLAN
IS BASED ON
“RULE CHAIN” IN 2020?
TEST CLASS METHODS ORDER
val setupRule = SetUpTearDownRule().addSetUp{...}
val activityRule = ActivityTestRule(MainActivity::class.java)
@get:Rule
val ruleChain = RuleChain.outerRule(setupRule)
.around(activityRule)
ORDER RULES WITH RULECHAIN
82
TEST CLASS METHODS ORDER
val setupRule = SetUpTearDownRule().addSetUp{...}
val activityRule = ActivityTestRule(MainActivity::class.java)
@get:Rule
val ruleChain = RuleChain.outerRule(setupRule)
.around(activityRule)
@get:Rule
val ruleSequence = RuleSequence(setupRule, activityRule)
REPLACE RULE CHAIN WITH RULE SEQUENCE
83
TEST CLASS METHODS ORDER
ADVANCES OF RULE SEQUENCE
84
▸ Friendly interface
TEST CLASS METHODS ORDER
ADVANCES OF RULE SEQUENCE
85
▸ Friendly interface
▸ Better work with test classes inheritance
TEST CLASS METHODS ORDER
ADVANCES OF RULE SEQUENCE
86
▸ Friendly interface
▸ Better work with test classes inheritance
▸ Three lists of prioritised rules (add, addFirst, addLast)
SOLUTIONS
TEST DATA IS DEFINED AFTER ACTIVITY LAUNCH
@BeforeClass
SetUps.actions
@Before
ActivityLaunched
@Test
87
SOLUTIONS
TEST DATA IS DEFINED AFTER ACTIVITY LAUNCH
@BeforeClass
SetUps.actions
@Before
ActivityLaunched
@Test
88
SOLUTIONS
▸ Set up test data in @BeforeClass
TEST DATA IS DEFINED AFTER ACTIVITY LAUNCH
SetUps.actions
@Before
@BeforeClass
89
ActivityLaunched
@Test
SOLUTIONS
▸ Set up test data in @BeforeClass
▸ LaunchActivity = false. Set up in @Before
TEST DATA IS DEFINED AFTER ACTIVITY LAUNCH
@BeforeClass
SetUps.actions
@Before@Before
90
ActivityLaunched
@Test
SOLUTIONS
▸ Set up test data in @BeforeClass
▸ LaunchActivity = false. Set up in @Before
▸ Custom SetUpRule + RuleSequence
TEST DATA IS DEFINED AFTER ACTIVITY LAUNCH
@BeforeClass
SetUps.actions
@Before
91
ActivityLaunched
@Test
SOLUTIONS
▸ Set up test data in @BeforeClass
▸ LaunchActivity = false. Set up in @Before
▸ Custom SetUpRule + RuleSequence (best way)
TEST DATA IS DEFINED AFTER ACTIVITY LAUNCH
@BeforeClass
SetUps.actions
@Before
92
ActivityLaunched
@Test
MISTAKE
TEST DATA IS DEFINED IN @BEFORECLASS OR @BEFORE METHOD FOR ALL @TEST
93
TEST DATA IS DEFINED IN @BEFORECLASS FOR ALL @TEST
class SomeTestClass {
companion object {
@BeforeClass @JvmStatic
fun setUpTestData(){
Api.clearChatMessages(contact.id)
Api.addChatMessage(contact.id, longMessage)
Api.addChatMessage(contact.id, simpleMessage)
Api.addChatMessage(contact.id, specialCharsMessage)
}
}
}
94
TEST DATA IS DEFINED IN @BEFORE METHOD FOR ALL @TEST
class SomeTestClass {
@Before
fun setUpTestData(){
Api.clearChatMessages(contact.id)
Api.addChatMessage(contact.id, longMessage)
Api.addChatMessage(contact.id, simpleMessage)
Api.addChatMessage(contact.id, specialCharsMessage)
}
}
95
SIMPLE TEXT ASSERTION BECOMES HARDER
@Test
fun assertSimpleMessage() {
ChatPage(contact)
.assertMessageDisplayed(simpleMessage.text)
}
96
SIMPLE TEXT ASSERTION BECOMES HARDER
@Test
fun assertSimpleMessage() {
ChatPage(contact)
.assertMessageDisplayed(simpleMessage.text)
}
97
DEMO
BAD WAY
99
TEST DATA DEFINITION FOR EACH TEST
SOLUTION: CUSTOM SETUP RULE + @SETUP & @TEARDOWN ANNOTATIONS
class SomeTestClass {
val setupRule = SetUpTearDownRule()
.addSetUp { Api.clearChatMessages(contact.id) }
.addSetUp("ADD_SIMPLE") { Api.addChatMessage(contact.id, simpleMessage) }
.addSetUp("ADD_LONG") { Api.addChatMessage(contact.id, longMessage) }
}
100
TEST DATA DEFINITION FOR EACH TEST
SOLUTION: CUSTOM SETUP RULE + @SETUP & @TEARDOWN ANNOTATIONS
class SomeTestClass {
val setupRule = SetUpTearDownRule()
.addSetUp { Api.clearChatMessages(contact.id) }
.addSetUp("ADD_SIMPLE") { Api.addChatMessage(contact.id, simpleMessage) }
.addSetUp("ADD_LONG") { Api.addChatMessage(contact.id, longMessage) }
}
101
TEST DATA DEFINITION FOR EACH TEST
SOLUTION: CUSTOM SETUP RULE + @SETUP & @TEARDOWN ANNOTATIONS
class SomeTestClass {
val setupRule = SetUpTearDownRule()
.addSetUp { Api.clearChatMessages(contact.id) }
.addSetUp("ADD_SIMPLE") { Api.addChatMessage(contact.id, simpleMessage) }
.addSetUp("ADD_LONG") { Api.addChatMessage(contact.id, longMessage) }
}
102
TEST DATA DEFINITION FOR EACH TEST
SOLUTION: CUSTOM SETUP RULE + @SETUP & @TEARDOWN ANNOTATIONS
class SomeTestClass {
val setupRule = SetUpTearDownRule()
.addSetUp { Api.clearChatMessages(contact.id) }
.addSetUp("ADD_SIMPLE") { Api.addChatMessage(contact.id, simpleMessage) }
.addSetUp("ADD_LONG") { Api.addChatMessage(contact.id, longMessage) }
}
103
TEST DATA DEFINITION FOR EACH TEST
SOLUTION: CUSTOM SETUP RULE + @SETUP & @TEARDOWN ANNOTATIONS
class SomeTestClass {
val setupRule = SetUpTearDownRule()
.addSetUp { Api.clearChatMessages(contact.id) }
.addSetUp("ADD_SIMPLE") { Api.addChatMessage(contact.id, simpleMessage) }
.addSetUp("ADD_LONG") { Api.addChatMessage(contact.id, longMessage) }
}
104
TEST DATA DEFINITION FOR EACH TEST
SOLUTION: CUSTOM SETUP RULE + @SETUP & @TEARDOWN ANNOTATIONS
class SomeTestClass {
val setupRule = SetUpTearDownRule()
.addSetUp { Api.clearChatMessages(contact.id) }
.addSetUp("ADD_SIMPLE") { Api.addChatMessage(contact.id, simpleMessage) }
.addSetUp("ADD_LONG") { Api.addChatMessage(contact.id, longMessage) }
}
105
TEST DATA DEFINITION FOR EACH TEST
SOLUTION: CUSTOM SETUP RULE + @SETUP & @TEARDOWN ANNOTATIONS
class SomeTestClass {
val setupRule = SetUpTearDownRule()
.addSetUp { Api.clearChatMessages(contact.id) }
.addSetUp("ADD_SIMPLE") { Api.addChatMessage(contact.id, simpleMessage) }
.addSetUp("ADD_LONG") { Api.addChatMessage(contact.id, longMessage) }
@SetUp("ADD_SIMPLE")
@Test
fun assertSimpleMessage() {
ChatPage(contact).assertMessageDisplayed(simpleMessage.text)
}
}
106
SIMPLE TEXT ASSERTION BECOMES EASY
@SetUp("ADD_SIMPLE")
@Test
fun assertSimpleMessage() {
ChatPage(contact)
.assertMessageDisplayed(simpleMessage.text)
}
107
TEST DATA DEFINITION FOR EACH TEST
SOLUTION: CUSTOM SETUP RULE + @SETUP & @TEARDOWN ANNOTATIONS
class SomeTestClass {
...
.addSetUp("ADD_SIMPLE") { Api.addChatMessage(contact.id, simpleMessage) }
.addSetUp("ADD_LONG") { Api.addChatMessage(contact.id, longMessage) }
@SetUp("ADD_SIMPLE")
@Test
fun assertBothMessagesDisplayed() {}
}
108
TEST DATA DEFINITION FOR EACH TEST
SOLUTION: CUSTOM SETUP RULE + @SETUP & @TEARDOWN ANNOTATIONS
class SomeTestClass {
...
.addSetUp("ADD_SIMPLE") { Api.addChatMessage(contact.id, simpleMessage) }
.addSetUp("ADD_LONG") { Api.addChatMessage(contact.id, longMessage) }
@SetUp("ADD_SIMPLE", "ADD_LONG")
@Test
fun assertBothMessagesDisplayed() {}
}
109
TEST DATA DEFINITION FOR EACH TEST
SOLUTION: CUSTOM SETUP RULE + @SETUP & @TEARDOWN ANNOTATIONS
class SomeTestClass {
...
.addSetUp("ADD_SIMPLE") { Api.addChatMessage(contact.id, simpleMessage) }
.addSetUp("ADD_LONG") { Api.addChatMessage(contact.id, longMessage) }
@SetUp("ADD_SIMPLE", "ADD_LONG")
@Test
fun assertBothMessagesDisplayed() {
ChatPage(contact)
.assertMessageDisplayed(simpleMessage.text)
.assertMessageDisplayed(longMessage.text)
}
110
DEMO
GOOD WAY
112
MISTAKE
TEST DATA IS RELATED WITH BUILD.SERIAL
Tests
runner
server
113
TEST DATA IS RELATED WITH BUILD.SERIAL
class SomeTestClass {
...
val user = TestData.getUser(Build.Serial)
}
114
TEST DATA IS RELATED WITH BUILD.SERIAL
Tests
runner
server
115
TEST DATA IS RELATED WITH BUILD.SERIAL
USER 1
Tests
runner
server
116
TEST DATA IS RELATED WITH BUILD.SERIAL
USER 1 USER 2 USER 3 USER 4 USER 5
Tests
runner
server
117
TEST DATA IS RELATED WITH BUILD.SERIAL
USER 1 USER 2 USER 3 USER 4 USER 5
Tests
runner
server
118
TEST DATA IS RELATED WITH BUILD.SERIAL
USER 1 USER 2 USER 3 USER 4 USER 5 USER 6
Tests
runner
server
119
TEST DATA IS RELATED WITH BUILD.SERIAL
https://openstf.io/
120
TEST DATA IS RELATED WITH BUILD.SERIAL
IF WE WANNA ADD OR CHANGE A DEVICE
121
TEST DATA IS RELATED WITH BUILD.SERIAL
IF WE WANNA ADD OR CHANGE A DEVICE
▸ Support changing in test code
122
TEST DATA IS RELATED WITH BUILD.SERIAL
IF WE WANNA ADD OR CHANGE A DEVICE
▸ Support changing in test code
▸ Configure test user to default test conditions
123
TEST DATA IS RELATED WITH BUILD.SERIAL
Tests
runner
server
124
TEST DATA IS RELATED WITH BUILD.SERIAL
Tests
runner
server
Test
data
server
125
TEST DATA IS RELATED WITH BUILD.SERIAL
Tests
runner
server
Test
data
server
126
TEST DATA IS RELATED WITH BUILD.SERIAL
ANY
USER
Tests
runner
server
Test
data
server
ANY
USER
ANY
USER
ANY
USER
ANY
USER
ANY
USER
127
WHAT TEST DATA SERVER PROVIDES TO US
Test
data
server
128
▸ Pool of test users and any other required objects
WHAT TEST DATA SERVER PROVIDES TO US
Test
data
server
129
▸ Pool of test users and any other required objects
▸ Data management via rest api
WHAT TEST DATA SERVER PROVIDES TO US
Test
data
server
130
▸ Pool of test users and any other required objects
▸ Data management via rest api
▸ Guarantee of test data uniqueness for separate test
WHAT TEST DATA SERVER PROVIDES TO US
Test
data
server
131
▸ Kotlin
▸ Spring Boot (https://spring.io/projects/spring-boot)
▸ JetBrains Exposed (https://github.com/JetBrains/Exposed)
WHAT TEST DATA SERVER FORMULA
Test
data
server
132
SOLUTION: TEST DATA SERVER
TEST DATA IS RELATED WITH BUILD.SERIAL 133
SOLUTION: TEST DATA SERVER
▸ No need to support devices
TEST DATA IS RELATED WITH BUILD.SERIAL 134
SOLUTION: TEST DATA SERVER
▸ No need to support devices
▸ Single point to control all test data
TEST DATA IS RELATED WITH BUILD.SERIAL 135
SOLUTION: TEST DATA SERVER
▸ No need to support devices
▸ Single point to control all test data
▸ Multiple test runs at the same time (CI tasks)
TEST DATA IS RELATED WITH BUILD.SERIAL 136
SOLUTION: TEST DATA SERVER
▸ No need to support devices
▸ Single point to control all test data
▸ Multiple test runs at the same time (CI tasks)
▸ It could be used by other test frameworks (iOS, Web, api tests)
TEST DATA IS RELATED WITH BUILD.SERIAL 137
TEST DATA USAGE
▸ Test data is used everywhere:

-> Strict data definition to test suite or test case

-> Store read-only data as constants
138
TEST DATA USAGE
▸ Test data is used everywhere:

-> Strict data definition to test suite or test case

-> Store read-only data as constants
▸ Test data is defined after activity launch

-> Use custom SetUpTearDown rule + RuleSequence
139
TEST DATA USAGE
▸ Test data is used everywhere:

-> Strict data definition to test suite or test case

-> Store read-only data as constants
▸ Test data is defined after activity launch

-> Use custom SetUpTearDown rule + RuleSequence
▸ Test data is defined in @BeforeClass or @Before method for all @Test
methods

-> Use SetUpTearDown rule + SetUp and TearDown annotations
140
TEST DATA USAGE
▸ Test data is used everywhere:

-> Strict data definition to test suite or test case

-> Store read-only data as constants
▸ Test data is defined after activity launch

-> Use custom SetUpTearDown rule + RuleSequence
▸ Test data is defined in @BeforeClass or @Before method for all @Test
methods

-> Use SetUpTearDown rule + SetUp and TearDown annotations
▸ Test user is related with device Build.Serial

-> Use TestData server
141
TEST FLOW MISTAKES 142
PREVIOUSLY ON HEISENBUG 143
PREVIOUSLY ON HEISENBUG 144
PREVIOUSLY ON HEISENBUG 145
PREVIOUSLY ON HEISENBUG 146
PREVIOUSLY ON HEISENBUG 147
DEMO
149
PREVIOUSLY ON HEISENBUG
COOL TEST
@Test
fun goodTest(){
FriendsListPage().openChat("Ross Geller")
ChatPage().clearHistory()
.sendMessage("test message")
}
150
IF I WANNA SEND ANOTHER MESSAGE
IS IT A GOOD IDEA?
@Test
fun sendMessageWithLink(){
FriendsListPage().openChat("Ross Geller”)
ChatPage().clearHistory()
.sendMessage("message with link vk.com")
}
151
IF I WANNA SEND ANOTHER MESSAGE
ACTUALLY IT ISN’T A GOOD IDEA!
@Test
fun sendMessageWithLink(){
FriendsListPage().openChat("Ross Geller”)
ChatPage().clearHistory()
.sendMessage("message with link vk.com")
}
152
MISTAKE
LONG TEST SCENARIO
153
MISTAKE
LONG TEST SCENARIO
▸ It’s a flaky way
154
MISTAKE
LONG TEST SCENARIO
▸ It’s a flaky way
▸ It takes a lot of time
155
MISTAKE
LONG TEST SCENARIO
▸ It’s a flaky way
▸ It takes a lot of time
▸ Code duplication
156
STOP WRITING LONG TESTS
MARTY, LOOK AT THE SHORT TEST!
157
LONG TEST SCENARIO
SOLUTION: SHORT TEST
@Test
fun sendMessageWithLink(){
FriendsListPage().openChat("Ross Geller”)
ChatPage().clearHistory()
.sendMessage("message with link vk.com")
}
158
LONG TEST SCENARIO
SOLUTION: SHORT TEST
@Test
fun sendMessageWithLink(){
FriendsListPage().openChat("Ross Geller”)
ChatPage().clearHistory()
ChatPage().sendMessage("message with link vk.com")
}
159
LONG TEST SCENARIO
SOLUTION: SHORT TEST
class ChatTest {
@Test
fun sendMessageWithLink() {
ChatPage().sendMessage("message with link vk.com")
}
}
160
SHORT TEST: LAUNCH TARGET ACTIVITY
class ChatTest {
@get:Rule
val activityRule = ActivityTestRule(MainActivity::class.java, false, false)
}
161
SHORT TEST: LAUNCH TARGET ACTIVITY
class ChatTest {
@get:Rule
val activityRule = ActivityTestRule(MainActivity::class.java, false, false)
}
162
class ChatTest {
@get:Rule
val activityRule = ActivityTestRule(ChatActivity::class.java, false, false)
}
SHORT TEST: LAUNCH TARGET ACTIVITY 163
SHORT TEST: LAUNCH TARGET ACTIVITY
class ChatTest {
@get:Rule
val activityRule = ActivityTestRule(ChatActivity::class.java, false, false)
@Before
fun prepareData() {
MessageRepository.clearChatMessages(contact.id)
val intent = Intent()
intent.putExtra("INTENT_CONTACT_ID_EXTRA_NAME", contact.id)
activityRule.launchActivity(intent)
}
}
164
SHORT TEST: LAUNCH TARGET ACTIVITY
class ChatTest {
@get:Rule
val activityRule = ActivityTestRule(ChatActivity::class.java, false, false)
@Before
fun prepareData() {
MessageRepository.clearChatMessages(contact.id)
val intent = Intent()
intent.putExtra("INTENT_CONTACT_ID_EXTRA_NAME", contact.id)
activityRule.launchActivity(intent)
}
}
165
SHORT TEST: LAUNCH TARGET ACTIVITY
class ChatTest {
@get:Rule
val activityRule = ActivityTestRule(ChatActivity::class.java, false, false)
@Before
fun prepareData() {
MessageRepository.clearChatMessages(contact.id)
val intent = Intent()
intent.putExtra("INTENT_CONTACT_ID_EXTRA_NAME", contact.id)
activityRule.launchActivity(intent)
}
}
166
SHORT TEST: LAUNCH TARGET ACTIVITY
class ChatTest {
@get:Rule
val activityRule = ActivityTestRule(ChatActivity::class.java, false, false)
@Before
fun prepareData() {
MessageRepository.clearChatMessages(contact.id)
val intent = Intent()
intent.putExtra("INTENT_CONTACT_ID_EXTRA_NAME", contact.id)
activityRule.launchActivity(intent)
}
}
167
SHORT TEST: LAUNCH TARGET ACTIVITY
class ChatTest {
@get:Rule
val activityRule = ActivityTestRule(ChatActivity::class.java, false, false)
@Before
fun prepareData() {
MessageRepository.clearChatMessages(contact.id)
val intent = Intent()
intent.putExtra("INTENT_CONTACT_ID_EXTRA_NAME", contact.id)
activityRule.launchActivity(intent)
}
}
168
SHORT TEST: LAUNCH TARGET ACTIVITY
class ChatTest {
@get:Rule
val activityRule = ActivityTestRule(ChatActivity::class.java, false, false)
@Before
fun prepareData() {
MessageRepository.clearChatMessages(contact.id)
val intent = Intent()
intent.putExtra("INTENT_CONTACT_ID_EXTRA_NAME", contact.id)
activityRule.launchActivity(intent)
}
@Test
fun sendMessageWithLink() {
ChatPage().sendMessage("message with link vk.com")
}
}
169
SHORT TEST: LAUNCH TARGET ACTIVITY
class ChatTest {
@get:Rule
val activityRule = ActivityTestRule(ChatActivity::class.java, false, false)
@Before
fun prepareData() {
MessageRepository.clearChatMessages(contact.id)
val intent = Intent()
intent.putExtra("INTENT_CONTACT_ID_EXTRA_NAME", contact.id)
activityRule.launchActivity(intent)
}
@Test
fun sendMessageWithLink() {...}
}
170
SHORT TEST: LAUNCH TARGET ACTIVITY
class ChatTest {
@get:Rule
val activityRule = ActivityTestRule(ChatActivity::class.java, false, false)
val setupRule = SetUpTearDownRule()
.addSetUp {
MessageRepository.clearChatMessages(contact.id)
val intent = Intent()
intent.putExtra("INTENT_CONTACT_ID_EXTRA_NAME", contact.id)
activityRule.launchActivity(intent)
}
@Test
fun sendMessageWithLink() {...}
}
171
SHORT TEST: LAUNCH TARGET ACTIVITY
class ChatTest {
@get:Rule
val activityRule = ActivityTestRule(ChatActivity::class.java, false, false)
val setupRule = SetUpTearDownRule()
.addSetUp {
MessageRepository.clearChatMessages(contact.id)
val intent = Intent()
intent.putExtra("INTENT_CONTACT_ID_EXTRA_NAME", contact.id)
activityRule.launchActivity(intent)
}
@Test
fun sendMessageWithLink() {...}
}
172
SHORT TEST: LAUNCH TARGET ACTIVITY
class ChatTest {
val activityRule = ActivityTestRule(ChatActivity::class.java, false, false)
val setupRule = SetUpTearDownRule()
.addSetUp {
MessageRepository.clearChatMessages(contact.id)
val intent = Intent()
intent.putExtra("INTENT_CONTACT_ID_EXTRA_NAME", contact.id)
activityRule.launchActivity(intent)
}
@get:Rule
val ruleSequence = RuleSequence(setupRule, activityRule)
@Test
fun sendMessageWithLink() {...}
}
173
LONG TEST 174
SHORT TEST: LAUNCH TARGET ACTIVITY 175
DEMO
177
LONG TEST SCENARIO
SOLUTION: SHORT TEST
class ChatTest {
@Test
fun sendMessageWithLink() {
ChatPage().sendMessage("message with link vk.com")
}
}
178
BLACKLIST CASE
WHAT CAN I DO WITH FRAGMENTS?
179
ANDROID FRAGMENTS 180
ANDROID FRAGMENTS 181
ANDROID FRAGMENTS 182
SettingsActivity
ANDROID FRAGMENTS 183
SettingsActivity
SettingsListFragment
ANDROID FRAGMENTS 184
SettingsActivity
BlacklistFragmentSettingsListFragment
SAME ACTIVITY BUT DIFFERENT FRAGMENTS 185
BLACKLIST DATA PREPARATION
class BlacklistTests {
...
@get:Rule
val ruleSequence = RuleSequence(setupRule, activityRule)
}
186
BLACKLIST DATA PREPARATION
class BlacklistTests {
...
@get:Rule
val ruleSequence = RuleSequence(setupRule, activityRule)
@Before
fun openFragment(){
activityRule.runOnUiThread {
SettingsFragmentNavigator.go(BlacklistFragment::class.java)
}
}
}
187
BLACKLIST DATA PREPARATION
class BlacklistTests {
...
@get:Rule
val ruleSequence = RuleSequence(setupRule, activityRule)
@Before
fun openFragment(){
activityRule.runOnUiThread {
SettingsFragmentNavigator.go(BlacklistFragment::class.java)
}
}
}
188
BLACKLIST DATA PREPARATION
class BlacklistTests {
...
@get:Rule
val ruleSequence = RuleSequence(setupRule, activityRule)
@Before
fun openFragment(){
activityRule.runOnUiThread {
SettingsFragmentNavigator.go(BlacklistFragment::class.java)
}
}
}
189
BLACKLIST DATA PREPARATION
class BlacklistTests {
...
@get:Rule
val ruleSequence = RuleSequence(setupRule, activityRule)
.addLast(SetUpTearDownRule().addSetUp {
activityRule.runOnUiThread {
SettingsFragmentNavigator.go(BlacklistFragment::class.java)
}
})
}
190
BLACKLIST CASE 191
BLACKLIST CASE 192
BLACKLIST CASE 193
BLACKLIST CASE 194
BLACKLIST CASE 195
BLACKLIST CASE 196
BLACKLIST CASE 197
BLACKLIST DATA PREPARATION
class BlacklistTests {
val activityRule = ActivityTestRule(SettingsActivity::class.java)
val setUpTearDownRule = SetUpTearDownRule()
.addSetUp {
ContactsRepositoty.clearBlacklist()
ContactsRepositoty.addToBlacklist(contact.id)
}
@get:Rule
val ruleSequence = RuleSequence(setupRule, activityRule)
}
198
BLACKLIST DATA PREPARATION
class BlacklistTests {
val activityRule = ActivityTestRule(SettingsActivity::class.java)
val setUpTearDownRule = SetUpTearDownRule()
.addSetUp {
ContactsRepositoty.clearBlacklist()
ContactsRepositoty.addToBlacklist(contact.id)
}
@get:Rule
val ruleSequence = RuleSequence(setupRule, activityRule)
}
199
BLACKLIST DATA PREPARATION
class BlacklistTests {
val activityRule = ActivityTestRule(SettingsActivity::class.java)
val setUpTearDownRule = SetUpTearDownRule()
.addSetUp {
ContactsRepositoty.clearBlacklist()
ContactsRepositoty.addToBlacklist(contact.id)
}
@get:Rule
val ruleSequence = RuleSequence(setupRule, activityRule)
}
200
BLACKLIST CASE 201
BLACKLIST CASE 202
DEMO
204
SOLUTION: USE INTERNAL NAVIGATION MECHANISM
FRAGMENTS NAVIGATION PROBLEM 205
activityRule.runOnUiThread {
SettingsFragmentNavigator.go(BlacklistFragment::class.java)
}
LOGIN
WHAT CAN I DO WITH LOGIN PROBLEM?
206
LOGIN
WHAT CAN I DO WITH LOGIN PROBLEM?
▸ Use authorisation via UI
207
LOGIN
WHAT CAN I DO WITH LOGIN PROBLEM?
▸ Use authorisation via UI
▸ Use internal authorisation mechanism
208
MISTAKE
USE AUTHORISATION VIA UI
209
MISTAKE
USE AUTHORISATION VIA UI
210
USE AUTHORISATION VIA UI
USE INTERNAL AUTHORISATION MECHANISM
AccountManager()
.login(
user.login,
user.password
)
211
INTERNAL AUTHORISATION MECHANISM
class ChatTest {
...
val setupRule = SetUpTearDownRule()
.addSetUp {
MessageRepository.clearChatMessages(contact.id)
val intent = Intent()
intent.putExtra("INTENT_CONTACT_ID_EXTRA_NAME", contact.id)
activityRule.launchActivity(intent)
}
@Test
fun sendMessageWithLink() {... }
}
212
INTERNAL AUTHORISATION MECHANISM
class ChatTest {
...
val setupRule = SetUpTearDownRule()
.addSetUp {
MessageRepository.clearChatMessages(contact.id)
...
}
@Test
fun sendMessageWithLink() {... }
}
213
INTERNAL AUTHORISATION MECHANISM
class ChatTest {
…
val setupRule = SetUpTearDownRule()
.addSetUp {
AccountManager(targetContext).login(user.login, user.password)
MessageRepository.clearChatMessages(contact.id)
...
}
@Test
fun sendMessageWithLink() {... }
}
214
SOLUTION: USE INTERNAL AUTHORISATION MECHANISM
LOGIN PROBLEM 215
AccountManager(targetContext).login(user.login, user.password)
TEST FLOW
I HAD THE LONGEST TEST EVER BUT NOW …
216
TEST FLOW
I HAD THE LONGEST TEST EVER BUT NOW …
▸ Long test scenario
217
TEST FLOW
I HAD THE LONGEST TEST EVER BUT NOW …
▸ Long test scenario 

-> Use short tests by opening target activity
218
TEST FLOW
I HAD THE LONGEST TEST EVER BUT NOW …
▸ Long test scenario 

-> Use short tests by opening target activity
▸ I navigate my tests to the Target Screen by UI actions 

219
TEST FLOW
I HAD THE LONGEST TEST EVER BUT NOW …
▸ Long test scenario 

-> Use short tests by opening target activity
▸ I navigate my tests to the Target Screen by UI actions 

-> Use internal navigation mechanism
220
TEST FLOW
I HAD THE LONGEST TEST EVER BUT NOW …
▸ Long test scenario 

-> Use short tests by opening target activity
▸ I navigate my tests to the Target Screen by UI actions

-> Use internal navigation mechanism
▸ Login by UI actions 

221
TEST FLOW
I HAD THE LONGEST TEST EVER BUT NOW …
▸ Long test scenario 

-> Use short tests by opening target activity
▸ I navigate my tests to the Target Screen by UI actions 

-> Use internal navigation mechanism
▸ Login by UI actions 

-> Do it by internal authorisation mechanism
222
TEST FLOW
▸ Long test scenario 

-> Use short tests by opening target activity
▸ I navigate my tests to the Target Screen by UI actions 

-> Use internal navigation mechanism
▸ Login by UI actions 

-> Do it by internal authorisation mechanism
223
I HAD THE LONGEST TEST EVER BUT NOW I HAVE THE SHORTEST ONE
TEST FRAMEWORK ARCHITECTURE 224
TEST FRAMEWORK ARCHITECTURE
MY FIRST TEST FRAMEWORK
225
226
TEST SUITES
TEST CLASSES
TEST DATA
TEST STEPSASSERTIONS
TEST ADAPTERS
227
TEST SUITES
TEST CLASSES
TEST DATA
TEST STEPSASSERTIONS
TEST ADAPTERS
228
TEST SUITES
TEST CLASSES
TEST DATA
TEST STEPSASSERTIONS
TEST ADAPTERS
STEPS
229
TEST SUITES
TEST CLASSES
TEST DATA
TEST STEPSASSERTIONS
TEST ADAPTERS
APP UI
ELEMENTS
STEPS
230
TEST SUITES
TEST CLASSES
TEST DATA
TEST STEPSASSERTIONS
TEST ADAPTERS
APP UI
ELEMENTS
STEPS
PAGE OBJECT CLASS
231
TEST FRAMEWORK ARCHITECTURE
MY FIRST TEST FRAMEWORK
232
TEST FRAMEWORK ARCHITECTURE
MY CURRENT TEST FRAMEWORK
233
MAKING YOUR OWN REPORT 234
REPORTING
IT WILL LOOKS LIKE THIS
235
236
BUT IT LOOKS LIKE THIS
237
OR LIKE THIS
REPORTING
WHY IT HAPPENS?
238
REPORTING
WHY IT HAPPENS?
▸ Support hell
239
REPORTING
WHY IT HAPPENS?
▸ Support hell
▸ Integration with CI hell
240
REPORTING
SOLUTION: USE ALREADY MADE LIBS & TOOLS
241
REPORTING
SOLUTION: USE ALREADY MADE LIBS & TOOLS
▸ Allure android
242
REPORTING
SOLUTION: USE ALREADY MADE LIBS & TOOLS
▸ Allure android
▸ Test runner report
243
REPORTING
SOLUTION: USE ALREADY MADE LIBS & TOOLS
▸ Allure android
▸ Test runner report
▸ Another already made report
244
TYPES OF MISTAKES WE’VE DISCOVERED
BACK TO THE PAST 245
TYPES OF MISTAKES WE’VE DISCOVERED
BACK TO THE PAST
▸ Test data usage
246
TEST DATA USAGE
▸ Test data is used everywhere:

-> Strict data definition to test suite or test case

-> Store read-only data as constants
▸ Test data is defined after activity launch

-> Use custom SetUpTearDown rule
▸ Test data is defined in @BeforeClass or @Before method for all @Test
methods

-> Use SetUpTearDown rule + SetUp and TearDown annotations
▸ Test user is related with device Build.Serial

-> Use TestData server
247
TYPES OF MISTAKES WE’VE DISCOVERED
BACK TO THE PAST
▸ Test data usage
▸ Test flow
248
TEST FLOW
▸ Long test scenario 

-> Use short tests by opening target activity
▸ I navigate my tests to the Target Screen by UI actions 

-> Use internal navigation mechanism
▸ Login by UI actions 

-> Do it by internal authorisation mechanism
249
I HAD THE LONGEST TEST EVER BUT NOW I HAVE THE SHORTEST ONE
TYPES OF MISTAKES WE’VE DISCOVERED
BACK TO THE PAST
▸ Test data usage
▸ Test flow
▸ Framework architecture
250
251SOLUTION: USE COMMON PRACTICES AND ADAPT IT
TEST SUITES
TEST CLASSES
TEST DATA
TEST STEPSASSERTIONS
TEST ADAPTERS
APP UI
ELEMENTS
STEPS
PAGE OBJECT CLASS
TYPES OF MISTAKES WE’VE DISCOVERED
BACK TO THE PAST
▸ Test data usage
▸ Test flow
▸ Framework architecture
▸ Reporting
252
REPORTING
SOLUTION: USE ALREADY MADE LIBS & TOOLS
▸ Allure android
▸ Test runner report
▸ Another already made report
253
GitHub:
1) Примеры кода
github.com/alex-tiurin/espresso-guide
2) Библиотека c Espresso DSL
github.com/alex-tiurin/espresso-page-object
Как подключить себе в проект?
repositories { jcenter() }
dependencies {
//espresso-page-object
androidTestImplementation 'com.atiurin.espresso:espressopageobject:0.1.16'
}

More Related Content

What's hot (20)

Breaking Dependencies to Allow Unit Testing
Breaking Dependencies to Allow Unit TestingBreaking Dependencies to Allow Unit Testing
Breaking Dependencies to Allow Unit Testing
 
3 j unit
3 j unit3 j unit
3 j unit
 
JUnit- A Unit Testing Framework
JUnit- A Unit Testing FrameworkJUnit- A Unit Testing Framework
JUnit- A Unit Testing Framework
 
Test driven development - JUnit basics and best practices
Test driven development - JUnit basics and best practicesTest driven development - JUnit basics and best practices
Test driven development - JUnit basics and best practices
 
Junit
JunitJunit
Junit
 
Unit testing best practices
Unit testing best practicesUnit testing best practices
Unit testing best practices
 
Software testing basics and its types
Software testing basics and its typesSoftware testing basics and its types
Software testing basics and its types
 
JUnit Pioneer
JUnit PioneerJUnit Pioneer
JUnit Pioneer
 
Unit test
Unit testUnit test
Unit test
 
JUnit 5
JUnit 5JUnit 5
JUnit 5
 
Unit testing patterns for concurrent code
Unit testing patterns for concurrent codeUnit testing patterns for concurrent code
Unit testing patterns for concurrent code
 
GeeCON 2012 Bad Tests, Good Tests
GeeCON 2012 Bad Tests, Good TestsGeeCON 2012 Bad Tests, Good Tests
GeeCON 2012 Bad Tests, Good Tests
 
Confitura 2012 Bad Tests, Good Tests
Confitura 2012 Bad Tests, Good TestsConfitura 2012 Bad Tests, Good Tests
Confitura 2012 Bad Tests, Good Tests
 
JUNit Presentation
JUNit PresentationJUNit Presentation
JUNit Presentation
 
Junit
JunitJunit
Junit
 
Quickly testing legacy code
Quickly testing legacy codeQuickly testing legacy code
Quickly testing legacy code
 
Building unit tests correctly
Building unit tests correctlyBuilding unit tests correctly
Building unit tests correctly
 
(Unit )-Testing for Joomla
(Unit )-Testing for Joomla(Unit )-Testing for Joomla
(Unit )-Testing for Joomla
 
Unit Testing Best Practices
Unit Testing Best PracticesUnit Testing Best Practices
Unit Testing Best Practices
 
Refactoring test code
Refactoring test codeRefactoring test code
Refactoring test code
 

Similar to Dont do it in android test automation

#codemotion2016: Everything you should know about testing to go with @pedro_g...
#codemotion2016: Everything you should know about testing to go with @pedro_g...#codemotion2016: Everything you should know about testing to go with @pedro_g...
#codemotion2016: Everything you should know about testing to go with @pedro_g...Sergio Arroyo
 
Dev labs alliance top 20 testng interview questions for sdet
Dev labs alliance top 20 testng interview questions for sdetDev labs alliance top 20 testng interview questions for sdet
Dev labs alliance top 20 testng interview questions for sdetdevlabsalliance
 
Adding unit tests with tSQLt to the database deployment pipeline
 Adding unit tests with tSQLt to the database deployment pipeline Adding unit tests with tSQLt to the database deployment pipeline
Adding unit tests with tSQLt to the database deployment pipelineEduardo Piairo
 
Pragmatic unittestingwithj unit
Pragmatic unittestingwithj unitPragmatic unittestingwithj unit
Pragmatic unittestingwithj unitliminescence
 
Some testing - Everything you should know about testing to go with @pedro_g_s...
Some testing - Everything you should know about testing to go with @pedro_g_s...Some testing - Everything you should know about testing to go with @pedro_g_s...
Some testing - Everything you should know about testing to go with @pedro_g_s...Sergio Arroyo
 
Adding unit tests to the database deployment pipeline
Adding unit tests to the database deployment pipelineAdding unit tests to the database deployment pipeline
Adding unit tests to the database deployment pipelineEduardo Piairo
 
More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)
More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)
More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)Jen Wong
 
Adding unit tests with tSQLt to the database deployment pipeline
Adding unit tests with tSQLt to the database deployment pipelineAdding unit tests with tSQLt to the database deployment pipeline
Adding unit tests with tSQLt to the database deployment pipelineEduardo Piairo
 
When assertthat(you).understandUnitTesting() fails
When assertthat(you).understandUnitTesting() failsWhen assertthat(you).understandUnitTesting() fails
When assertthat(you).understandUnitTesting() failsMartin Skurla
 
Introduction to Software Testing
Introduction to Software TestingIntroduction to Software Testing
Introduction to Software TestingSergio Arroyo
 
Test Driven Development with Sql Server
Test Driven Development with Sql ServerTest Driven Development with Sql Server
Test Driven Development with Sql ServerDavid P. Moore
 
TestNG - The Next Generation of Unit Testing
TestNG - The Next Generation of Unit TestingTestNG - The Next Generation of Unit Testing
TestNG - The Next Generation of Unit TestingBethmi Gunasekara
 
Adding unit tests to the database deployment pipeline
Adding unit tests to the database deployment pipelineAdding unit tests to the database deployment pipeline
Adding unit tests to the database deployment pipelineEduardo Piairo
 
Just Do It! ColdBox Integration Testing
Just Do It! ColdBox Integration TestingJust Do It! ColdBox Integration Testing
Just Do It! ColdBox Integration TestingOrtus Solutions, Corp
 
Journey's diary developing a framework using tdd
Journey's diary   developing a framework using tddJourney's diary   developing a framework using tdd
Journey's diary developing a framework using tddeduardomg23
 
Testing Django Applications
Testing Django ApplicationsTesting Django Applications
Testing Django ApplicationsGareth Rushgrove
 
Developer Tests - Things to Know (Vilnius JUG)
Developer Tests - Things to Know (Vilnius JUG)Developer Tests - Things to Know (Vilnius JUG)
Developer Tests - Things to Know (Vilnius JUG)vilniusjug
 
Никита Галкин "Testing in Frontend World"
Никита Галкин "Testing in Frontend World"Никита Галкин "Testing in Frontend World"
Никита Галкин "Testing in Frontend World"Fwdays
 

Similar to Dont do it in android test automation (20)

#codemotion2016: Everything you should know about testing to go with @pedro_g...
#codemotion2016: Everything you should know about testing to go with @pedro_g...#codemotion2016: Everything you should know about testing to go with @pedro_g...
#codemotion2016: Everything you should know about testing to go with @pedro_g...
 
Dev labs alliance top 20 testng interview questions for sdet
Dev labs alliance top 20 testng interview questions for sdetDev labs alliance top 20 testng interview questions for sdet
Dev labs alliance top 20 testng interview questions for sdet
 
Adding unit tests with tSQLt to the database deployment pipeline
 Adding unit tests with tSQLt to the database deployment pipeline Adding unit tests with tSQLt to the database deployment pipeline
Adding unit tests with tSQLt to the database deployment pipeline
 
Pragmatic unittestingwithj unit
Pragmatic unittestingwithj unitPragmatic unittestingwithj unit
Pragmatic unittestingwithj unit
 
Some testing - Everything you should know about testing to go with @pedro_g_s...
Some testing - Everything you should know about testing to go with @pedro_g_s...Some testing - Everything you should know about testing to go with @pedro_g_s...
Some testing - Everything you should know about testing to go with @pedro_g_s...
 
Unit testing
Unit testingUnit testing
Unit testing
 
Adding unit tests to the database deployment pipeline
Adding unit tests to the database deployment pipelineAdding unit tests to the database deployment pipeline
Adding unit tests to the database deployment pipeline
 
Testing 101
Testing 101Testing 101
Testing 101
 
More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)
More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)
More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)
 
Adding unit tests with tSQLt to the database deployment pipeline
Adding unit tests with tSQLt to the database deployment pipelineAdding unit tests with tSQLt to the database deployment pipeline
Adding unit tests with tSQLt to the database deployment pipeline
 
When assertthat(you).understandUnitTesting() fails
When assertthat(you).understandUnitTesting() failsWhen assertthat(you).understandUnitTesting() fails
When assertthat(you).understandUnitTesting() fails
 
Introduction to Software Testing
Introduction to Software TestingIntroduction to Software Testing
Introduction to Software Testing
 
Test Driven Development with Sql Server
Test Driven Development with Sql ServerTest Driven Development with Sql Server
Test Driven Development with Sql Server
 
TestNG - The Next Generation of Unit Testing
TestNG - The Next Generation of Unit TestingTestNG - The Next Generation of Unit Testing
TestNG - The Next Generation of Unit Testing
 
Adding unit tests to the database deployment pipeline
Adding unit tests to the database deployment pipelineAdding unit tests to the database deployment pipeline
Adding unit tests to the database deployment pipeline
 
Just Do It! ColdBox Integration Testing
Just Do It! ColdBox Integration TestingJust Do It! ColdBox Integration Testing
Just Do It! ColdBox Integration Testing
 
Journey's diary developing a framework using tdd
Journey's diary   developing a framework using tddJourney's diary   developing a framework using tdd
Journey's diary developing a framework using tdd
 
Testing Django Applications
Testing Django ApplicationsTesting Django Applications
Testing Django Applications
 
Developer Tests - Things to Know (Vilnius JUG)
Developer Tests - Things to Know (Vilnius JUG)Developer Tests - Things to Know (Vilnius JUG)
Developer Tests - Things to Know (Vilnius JUG)
 
Никита Галкин "Testing in Frontend World"
Никита Галкин "Testing in Frontend World"Никита Галкин "Testing in Frontend World"
Никита Галкин "Testing in Frontend World"
 

Recently uploaded

HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comFatema Valibhai
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...kellynguyen01
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️Delhi Call girls
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerThousandEyes
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsArshad QA
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsAndolasoft Inc
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...gurkirankumar98700
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...OnePlan Solutions
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about usDynamic Netsoft
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...MyIntelliSource, Inc.
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...panagenda
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software DevelopersVinodh Ram
 
Active Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdfActive Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdfCionsystems
 
Diamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionDiamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionSolGuruz
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdfWave PLM
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsJhone kinadey
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Modelsaagamshah0812
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...ICS
 

Recently uploaded (20)

HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.js
 
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS LiveVip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about us
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
 
Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software Developers
 
Active Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdfActive Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdf
 
Diamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionDiamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with Precision
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 

Dont do it in android test automation

  • 1. DON’T DO IT IN ANDROID TEST AUTOMATION ALEKSEI TIURIN, VK
  • 2. WHO I AM? LEAD AUTOMATION ENGINEER AT VK •vk.com/alex_tiurin •github.com/alex-tiurin •linkedin.com/in/aleksei-tiurin 2
  • 4. PREVIOUSLY ON HEISENBUG РЕШАЕМ ПРОБЛЕМЫ  ESPRESSO АВТОТЕСТОВ ANDROID В РЕАЛЬНОМ МИРЕ 4
  • 5. WHAT ARE WE GONNA TALK ABOUT? TYPES OF MISTAKES 5
  • 6. WHAT ARE WE GONNA TALK ABOUT? TYPES OF MISTAKES ▸ Test data usage 6
  • 7. WHAT ARE WE GONNA TALK ABOUT? TYPES OF MISTAKES ▸ Test data usage ▸ Test flow 7
  • 8. WHAT ARE WE GONNA TALK ABOUT? TYPES OF MISTAKES ▸ Test data usage ▸ Test flow ▸ Test framework architecture 8
  • 9. WHAT ARE WE GONNA TALK ABOUT? TYPES OF MISTAKES ▸ Test data usage ▸ Test flow ▸ Test framework architecture ▸ Reporting 9
  • 10.
  • 13. TEST CLASS SKELETON class SomeTestClass { ... } 13
  • 14. TEST CLASS SKELETON class SomeTestClass { companion object { @BeforeClass @JvmStatic fun executeBeforeAllTests(){} } ... } 14
  • 15. TEST CLASS SKELETON class SomeTestClass { companion object { @BeforeClass @JvmStatic fun executeBeforeAllTests(){} } @Before fun executeBeforeEachTest(){} ... } 15
  • 16. TEST CLASS SKELETON class SomeTestClass { companion object { @BeforeClass @JvmStatic fun executeBeforeAllTests(){} }
 @Before fun executeBeforeEachTest(){} @Test fun assertSmthTest(){} } 16
  • 17. TEST CLASS SKELETON class SomeTestClass { companion object { @BeforeClass @JvmStatic fun executeBeforeAllTests(){} }
 @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) @Before fun executeBeforeEachTest(){} @Test fun assertSmthTest(){} } 17
  • 19. TEST DATA USAGE MARTY, STOP USING TEST DATA EVERYWHERE!!! 19
  • 20. TEST DATA IS USED EVERYWHERE object TestData { val chatId = "..." val documentId = "..." } 20
  • 21. TEST DATA IS USED EVERYWHERE object TestData { val chatId = "..." val documentId = "..." } // Somewhere in user steps fun sendDocInChat(user: User){ addDocToUser(user, TestData.documentId) sendDoc(TestData.chatId, TestData.documentId) } 21
  • 22. TEST DATA IS USED EVERYWHERE object TestData { val chatId = "..." val documentId = "..." } // Somewhere in user steps fun sendDocInChat(user: User){ addDocToUser(user, TestData.documentId) sendDoc(TestData.chatId, TestData.documentId) } // Somewhere in test class @Test fun someTest(){ sendDocInChat(user) } 22
  • 23. TEST DATA IS USED EVERYWHERE WHAT’S A PROBLEM 23
  • 24. TEST DATA IS USED EVERYWHERE WHAT’S A PROBLEM ▸ Lost a control on test data 24
  • 25. TEST DATA IS USED EVERYWHERE WHAT’S A PROBLEM ▸ Lost a control on test data ▸ Unexpected test application condition 25
  • 26. TEST DATA IS USED EVERYWHERE WHAT’S A PROBLEM ▸ Lost a control on test data ▸ Unexpected test application condition ▸ The same data can be used by several tests at the same time 26
  • 27. TEST SUITES TEST CLASSES TEST STEPSASSERTIONS TEST ADAPTERS WHERE TEST DATA SHOULD BE USED? 27
  • 28. TEST SUITES TEST CLASSES TEST DATA TEST STEPSASSERTIONS TEST ADAPTERS 28
  • 29. PREVIOUSLY ON HEISENBUG Д. БУЗДИН - КАК ПОСТРОИТЬ СВОЙ ФРЕЙМВОРК ДЛЯ АВТОТЕСТОВ? 29
  • 30. SOLUTION ▸ Restrict the scope of test data definition to test class / test suite TEST DATA USAGE 30
  • 31. TEST DATA IS USED EVERYWHERE object TestData { val chatId = "..." val documentId = "..." } // Somewhere in user steps fun sendDocInChat(user: User){ addDocToUser(user, TestData.documentId) sendDoc(TestData.chatId, TestData.documentId) } 31
  • 32. RESTRICT THE SCOPE OF TEST DATA DEFINITION TO TEST CLASS / TEST SUITE object TestData { val chatId = "..." val documentId = "..." } // Somewhere in user steps fun sendDocInChat(user: User, docId: String, chatId: String){ addDocToUser(user, docId) sendDoc(chatId, docId) } 32
  • 33. object TestData { val chatId = "..." val documentId = "..." } // Somewhere in user steps fun sendDocInChat(user: User, docId: String, chatId: String){ addDocToUser(user, docId) sendDoc(chatId, docId) } 33RESTRICT THE SCOPE OF TEST DATA DEFINITION TO TEST CLASS / TEST SUITE
  • 34. object TestData { val chatId = "..." val documentId = "..." } // Somewhere in user steps fun sendDocInChat(user: User, docId: String, chatId: String){ addDocToUser(user, docId) sendDoc(chatId, docId) } // Somewhere in test class @Test fun someTest(){ sendDocInChat(user, documentId, chatId) } 34RESTRICT THE SCOPE OF TEST DATA DEFINITION TO TEST CLASS / TEST SUITE
  • 35. SOLUTION ▸ Restrict the scope of test data definition to test class / test suite TEST DATA USAGE 35
  • 36. SOLUTION ▸ Restrict the scope of test data definition to test class / test suite ▸ Store read-only data as project constants TEST DATA USAGE 36
  • 37. STORE READ-ONLY DATA AS PROJECT CONSTANTS object TestData { // editable value, should be calculated somehow val chatId = TestDataManager.createChat() val documentId = "..." } // Somewhere in test class @Test fun someTest(){ sendDocInChat(user, documentId, chatId) } 37
  • 38. STORE READ-ONLY DATA AS PROJECT CONSTANTS object TestData { // editable value, should be calculated somehow val chatId = TestDataManager.createChat() val documentId = TestData.documentId // read-only value } // Somewhere in test class @Test fun someTest(){ sendDocInChat(user, documentId, chatId) } 38
  • 39. MISTAKE TEST DATA IS DEFINED AFTER ACTIVITY LAUNCH 39
  • 40. MISTAKE TEST DATA IS DEFINED AFTER ACTIVITY LAUNCH @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) @Before fun setUp(){ Api.addChatMessage(contact.id, "text") } 40
  • 41. TEST CLASS METHODS ORDER @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) @Before fun setUp(){ Api.addChatMessage(contact.id, "text") } REASON: @BEFORE EXECUTES AFTER ACTIVITY LAUNCHED 41
  • 42. TEST CLASS METHODS ORDER @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) @Before fun setUp(){ Api.addChatMessage(contact.id, "text") } ActivityLaunched REASON: @BEFORE EXECUTES AFTER ACTIVITY LAUNCHED 42
  • 43. TEST CLASS METHODS ORDER @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) @Before fun setUp(){ Api.addChatMessage(contact.id, "text") } REASON: @BEFORE EXECUTES AFTER ACTIVITY LAUNCHED @Before 43 ActivityLaunched
  • 44. TEST CLASS METHODS ORDER @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) @Before fun setUp(){ Api.addChatMessage(contact.id, "text") } REASON: @BEFORE EXECUTES AFTER ACTIVITY LAUNCHED @Test 44 @Before ActivityLaunched
  • 45. TEST CLASS METHODS ORDER @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) @Before fun setUp(){ Api.addChatMessage(contact.id, "text") } REASON: @BEFORE EXECUTES AFTER ACTIVITY LAUNCHED 45
  • 46. WHEN TEST DATA SHOULD BE DEFINED 46
  • 47. WHEN TEST DATA SHOULD BE DEFINED DEFINE TEST DATA 47
  • 48. WHEN TEST DATA SHOULD BE DEFINED LAUNCH ACTIVITY DEFINE TEST DATA 48
  • 49. WHEN TEST DATA SHOULD BE DEFINED LAUNCH ACTIVITY DEFINE TEST DATA @TEST 1 49
  • 50. WHEN TEST DATA SHOULD BE DEFINED LAUNCH ACTIVITY DEFINE TEST DATA @TEST 1 @TEST 2 50
  • 51. TEST CLASS METHODS ORDER @BeforeClass @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) companion object { @BeforeClass @JvmStatic fun setUpTestData(){ Api.addChatMessage(contact.id, "text") } } SOLUTION #1: SET UP DATA IN @BEFORECLASS 51
  • 52. TEST CLASS METHODS ORDER @BeforeClass @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) companion object { @BeforeClass @JvmStatic fun setUpTestData(){ Api.addChatMessage(contact.id, "text") } } SOLUTION #1: SET UP DATA IN @BEFORECLASS 52
  • 53. TEST CLASS METHODS ORDER @BeforeClass @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) companion object { @BeforeClass @JvmStatic fun setUpTestData(){ Api.addChatMessage(contact.id, "text") } } SOLUTION #1: SET UP DATA IN @BEFORECLASS ActivityLaunched @Test 53
  • 54. TEST CLASS METHODS ORDER @BeforeClass @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) companion object { @BeforeClass @JvmStatic fun setUpTestData(){ Api.addChatMessage(contact.id, "text") } } SOLUTION #1: SET UP DATA IN @BEFORECLASS ActivityLaunched @Test 54
  • 55. TEST CLASS METHODS ORDER @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java, initialTouchMode = false, launchActivity = false) @Before fun setUp(){ Api.addChatMessage(contact.id, "text") activityRule.launchActivity(Intent()) } SOLUTION #2: LAUNCH ACTIVITY = FALSE 55
  • 56. TEST CLASS METHODS ORDER @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java, initialTouchMode = false, launchActivity = false) @Before fun setUp(){ Api.addChatMessage(contact.id, "text") activityRule.launchActivity(Intent()) } SOLUTION #2: LAUNCH ACTIVITY = FALSE 56
  • 57. TEST CLASS METHODS ORDER @BeforeClass @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java, initialTouchMode = false, launchActivity = false) @Before fun setUp(){ Api.addChatMessage(contact.id, "text") activityRule.launchActivity(Intent()) } SOLUTION #2: LAUNCH ACTIVITY = FALSE 57
  • 58. TEST CLASS METHODS ORDER @BeforeClass @Before @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java, initialTouchMode = false, launchActivity = false) @Before fun setUp(){ Api.addChatMessage(contact.id, "text") activityRule.launchActivity(Intent()) } SOLUTION #2: LAUNCH ACTIVITY = FALSE 58
  • 59. TEST CLASS METHODS ORDER @BeforeClass @Before @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java, initialTouchMode = false, launchActivity = false) @Before fun setUp(){ Api.addChatMessage(contact.id, "text") activityRule.launchActivity(Intent()) } SOLUTION #2: LAUNCH ACTIVITY = FALSE 59
  • 60. TEST CLASS METHODS ORDER @BeforeClass @Before ActivityLaunched @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java, initialTouchMode = false, launchActivity = false) @Before fun setUp(){ Api.addChatMessage(contact.id, "text") activityRule.launchActivity(Intent()) } SOLUTION #2: LAUNCH ACTIVITY = FALSE 60
  • 61. TEST CLASS METHODS ORDER @BeforeClass @Before ActivityLaunched @Test @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java, initialTouchMode = false, launchActivity = false) @Before fun setUp(){ Api.addChatMessage(contact.id, "text") activityRule.launchActivity(Intent()) } SOLUTION #2: LAUNCH ACTIVITY = FALSE 61
  • 62. TEST CLASS METHODS ORDER SOLUTION #3: CUSTOM SETUP RULE class SetUpTearDownRule : TestWatcher() { val setUps = mutableListOf<Condition>() val tearDowns = mutableListOf<Condition>() } 62
  • 63. TEST CLASS METHODS ORDER SOLUTION #3: CUSTOM SETUP RULE class SetUpTearDownRule : TestWatcher() { val setUps = mutableListOf<Condition>() val tearDowns = mutableListOf<Condition>() } 63
  • 64. TEST CLASS METHODS ORDER SOLUTION #3: CUSTOM SETUP RULE class SetUpTearDownRule : TestWatcher() { val setUps = mutableListOf<Condition>() val tearDowns = mutableListOf<Condition>() } 64
  • 65. TEST CLASS METHODS ORDER class SetUpTearDownRule : TestWatcher() { private val setUps = mutableListOf<Condition>() inner class Condition( val counter: Int, val key: String, val actions: () -> Unit ) } SOLUTION #3: CUSTOM SETUP RULE 65
  • 66. TEST CLASS METHODS ORDER class SomeTestClass { @get:Rule val setupRule = SetUpTearDownRule() .addSetUp { Api.addChatMessage(contact.id, "text") } ... } SOLUTION #3: CUSTOM SETUP RULE 66
  • 67. TEST CLASS METHODS ORDER class SomeTestClass { @get:Rule val setupRule = SetUpTearDownRule() .addSetUp { Api.addChatMessage(contact.id, "text") } ... } SOLUTION #3: CUSTOM SETUP RULE 67
  • 68. TEST CLASS METHODS ORDER class SomeTestClass { @get:Rule val setupRule = SetUpTearDownRule() .addSetUp { Api.addChatMessage(contact.id, "text") } @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) ... } @BeforeClassSOLUTION #3: CUSTOM SETUP RULE 68
  • 69. TEST CLASS METHODS ORDER class SomeTestClass { @get:Rule val setupRule = SetUpTearDownRule() .addSetUp { Api.addChatMessage(contact.id, "text") } @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) ... } @BeforeClass SetUps.actions SOLUTION #3: CUSTOM SETUP RULE 69
  • 70. TEST CLASS METHODS ORDER class SomeTestClass { @get:Rule val setupRule = SetUpTearDownRule() .addSetUp { Api.addChatMessage(contact.id, "text") } @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) ... } @BeforeClass SetUps.actions ActivityLaunched SOLUTION #3: CUSTOM SETUP RULE 70
  • 71. TEST CLASS METHODS ORDER class SomeTestClass { @get:Rule val setupRule = SetUpTearDownRule() .addSetUp { Api.addChatMessage(contact.id, "text") } @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) ... } @BeforeClass SetUps.actions ActivityLaunched @Before SOLUTION #3: CUSTOM SETUP RULE 71
  • 72. TEST CLASS METHODS ORDER class SomeTestClass { @get:Rule val setupRule = SetUpTearDownRule() .addSetUp { Api.addChatMessage(contact.id, "text") } @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) ... } @BeforeClass SetUps.actions ActivityLaunched @Before @Test SOLUTION #3: CUSTOM SETUP RULE 72
  • 73. TEST CLASS METHODS ORDER class SomeTestClass { @get:Rule val setupRule = SetUpTearDownRule() .addSetUp { Api.addChatMessage(contact.id, "text") } @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) ... } SOLUTION #3: CUSTOM SETUP RULE 73
  • 75. TEST CLASS METHODS ORDER class SomeTestClass { @get:Rule val setupRule = SetUpTearDownRule() .addSetUp { Api.addChatMessage(contact.id, "text") } @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) ... } SOLUTION #3 DOESN’T WORK ALWAYS! WHY? 75
  • 76. TEST CLASS METHODS ORDER class SomeTestClass { @get:Rule val setupRule = SetUpTearDownRule() .addSetUp { Api.addChatMessage(contact.id, "text") } @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) ... } SOLUTION #3 DOESN’T WORK ALWAYS! WHY? 76
  • 77. TEST CLASS METHODS ORDER @get:Rule val setupRule = SetUpTearDownRule().addSetUp{...} @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) @get:Rule val ruleChain = RuleChain.outerRule(setupRule) .around(activityRule) ORDER RULES WITH RULECHAIN 77
  • 78. TEST CLASS METHODS ORDER @get:Rule val setupRule = SetUpTearDownRule().addSetUp{...} @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java) @get:Rule val ruleChain = RuleChain.outerRule(setupRule) .around(activityRule) ORDER RULES WITH RULECHAIN 78
  • 79. TEST CLASS METHODS ORDER val setupRule = SetUpTearDownRule().addSetUp{...} val activityRule = ActivityTestRule(MainActivity::class.java) @get:Rule val ruleChain = RuleChain.outerRule(setupRule) .around(activityRule) ORDER RULES WITH RULECHAIN 79
  • 80. 80 MARTY, ARE YOU REALLY TELLING ME YOUR PLAN IS BASED ON “BACK TO THE FUTURE”?
  • 81. 81 MARTY, ARE YOU REALLY TELLING ME YOUR PLAN IS BASED ON “RULE CHAIN” IN 2020?
  • 82. TEST CLASS METHODS ORDER val setupRule = SetUpTearDownRule().addSetUp{...} val activityRule = ActivityTestRule(MainActivity::class.java) @get:Rule val ruleChain = RuleChain.outerRule(setupRule) .around(activityRule) ORDER RULES WITH RULECHAIN 82
  • 83. TEST CLASS METHODS ORDER val setupRule = SetUpTearDownRule().addSetUp{...} val activityRule = ActivityTestRule(MainActivity::class.java) @get:Rule val ruleChain = RuleChain.outerRule(setupRule) .around(activityRule) @get:Rule val ruleSequence = RuleSequence(setupRule, activityRule) REPLACE RULE CHAIN WITH RULE SEQUENCE 83
  • 84. TEST CLASS METHODS ORDER ADVANCES OF RULE SEQUENCE 84 ▸ Friendly interface
  • 85. TEST CLASS METHODS ORDER ADVANCES OF RULE SEQUENCE 85 ▸ Friendly interface ▸ Better work with test classes inheritance
  • 86. TEST CLASS METHODS ORDER ADVANCES OF RULE SEQUENCE 86 ▸ Friendly interface ▸ Better work with test classes inheritance ▸ Three lists of prioritised rules (add, addFirst, addLast)
  • 87. SOLUTIONS TEST DATA IS DEFINED AFTER ACTIVITY LAUNCH @BeforeClass SetUps.actions @Before ActivityLaunched @Test 87
  • 88. SOLUTIONS TEST DATA IS DEFINED AFTER ACTIVITY LAUNCH @BeforeClass SetUps.actions @Before ActivityLaunched @Test 88
  • 89. SOLUTIONS ▸ Set up test data in @BeforeClass TEST DATA IS DEFINED AFTER ACTIVITY LAUNCH SetUps.actions @Before @BeforeClass 89 ActivityLaunched @Test
  • 90. SOLUTIONS ▸ Set up test data in @BeforeClass ▸ LaunchActivity = false. Set up in @Before TEST DATA IS DEFINED AFTER ACTIVITY LAUNCH @BeforeClass SetUps.actions @Before@Before 90 ActivityLaunched @Test
  • 91. SOLUTIONS ▸ Set up test data in @BeforeClass ▸ LaunchActivity = false. Set up in @Before ▸ Custom SetUpRule + RuleSequence TEST DATA IS DEFINED AFTER ACTIVITY LAUNCH @BeforeClass SetUps.actions @Before 91 ActivityLaunched @Test
  • 92. SOLUTIONS ▸ Set up test data in @BeforeClass ▸ LaunchActivity = false. Set up in @Before ▸ Custom SetUpRule + RuleSequence (best way) TEST DATA IS DEFINED AFTER ACTIVITY LAUNCH @BeforeClass SetUps.actions @Before 92 ActivityLaunched @Test
  • 93. MISTAKE TEST DATA IS DEFINED IN @BEFORECLASS OR @BEFORE METHOD FOR ALL @TEST 93
  • 94. TEST DATA IS DEFINED IN @BEFORECLASS FOR ALL @TEST class SomeTestClass { companion object { @BeforeClass @JvmStatic fun setUpTestData(){ Api.clearChatMessages(contact.id) Api.addChatMessage(contact.id, longMessage) Api.addChatMessage(contact.id, simpleMessage) Api.addChatMessage(contact.id, specialCharsMessage) } } } 94
  • 95. TEST DATA IS DEFINED IN @BEFORE METHOD FOR ALL @TEST class SomeTestClass { @Before fun setUpTestData(){ Api.clearChatMessages(contact.id) Api.addChatMessage(contact.id, longMessage) Api.addChatMessage(contact.id, simpleMessage) Api.addChatMessage(contact.id, specialCharsMessage) } } 95
  • 96. SIMPLE TEXT ASSERTION BECOMES HARDER @Test fun assertSimpleMessage() { ChatPage(contact) .assertMessageDisplayed(simpleMessage.text) } 96
  • 97. SIMPLE TEXT ASSERTION BECOMES HARDER @Test fun assertSimpleMessage() { ChatPage(contact) .assertMessageDisplayed(simpleMessage.text) } 97
  • 99. 99
  • 100. TEST DATA DEFINITION FOR EACH TEST SOLUTION: CUSTOM SETUP RULE + @SETUP & @TEARDOWN ANNOTATIONS class SomeTestClass { val setupRule = SetUpTearDownRule() .addSetUp { Api.clearChatMessages(contact.id) } .addSetUp("ADD_SIMPLE") { Api.addChatMessage(contact.id, simpleMessage) } .addSetUp("ADD_LONG") { Api.addChatMessage(contact.id, longMessage) } } 100
  • 101. TEST DATA DEFINITION FOR EACH TEST SOLUTION: CUSTOM SETUP RULE + @SETUP & @TEARDOWN ANNOTATIONS class SomeTestClass { val setupRule = SetUpTearDownRule() .addSetUp { Api.clearChatMessages(contact.id) } .addSetUp("ADD_SIMPLE") { Api.addChatMessage(contact.id, simpleMessage) } .addSetUp("ADD_LONG") { Api.addChatMessage(contact.id, longMessage) } } 101
  • 102. TEST DATA DEFINITION FOR EACH TEST SOLUTION: CUSTOM SETUP RULE + @SETUP & @TEARDOWN ANNOTATIONS class SomeTestClass { val setupRule = SetUpTearDownRule() .addSetUp { Api.clearChatMessages(contact.id) } .addSetUp("ADD_SIMPLE") { Api.addChatMessage(contact.id, simpleMessage) } .addSetUp("ADD_LONG") { Api.addChatMessage(contact.id, longMessage) } } 102
  • 103. TEST DATA DEFINITION FOR EACH TEST SOLUTION: CUSTOM SETUP RULE + @SETUP & @TEARDOWN ANNOTATIONS class SomeTestClass { val setupRule = SetUpTearDownRule() .addSetUp { Api.clearChatMessages(contact.id) } .addSetUp("ADD_SIMPLE") { Api.addChatMessage(contact.id, simpleMessage) } .addSetUp("ADD_LONG") { Api.addChatMessage(contact.id, longMessage) } } 103
  • 104. TEST DATA DEFINITION FOR EACH TEST SOLUTION: CUSTOM SETUP RULE + @SETUP & @TEARDOWN ANNOTATIONS class SomeTestClass { val setupRule = SetUpTearDownRule() .addSetUp { Api.clearChatMessages(contact.id) } .addSetUp("ADD_SIMPLE") { Api.addChatMessage(contact.id, simpleMessage) } .addSetUp("ADD_LONG") { Api.addChatMessage(contact.id, longMessage) } } 104
  • 105. TEST DATA DEFINITION FOR EACH TEST SOLUTION: CUSTOM SETUP RULE + @SETUP & @TEARDOWN ANNOTATIONS class SomeTestClass { val setupRule = SetUpTearDownRule() .addSetUp { Api.clearChatMessages(contact.id) } .addSetUp("ADD_SIMPLE") { Api.addChatMessage(contact.id, simpleMessage) } .addSetUp("ADD_LONG") { Api.addChatMessage(contact.id, longMessage) } } 105
  • 106. TEST DATA DEFINITION FOR EACH TEST SOLUTION: CUSTOM SETUP RULE + @SETUP & @TEARDOWN ANNOTATIONS class SomeTestClass { val setupRule = SetUpTearDownRule() .addSetUp { Api.clearChatMessages(contact.id) } .addSetUp("ADD_SIMPLE") { Api.addChatMessage(contact.id, simpleMessage) } .addSetUp("ADD_LONG") { Api.addChatMessage(contact.id, longMessage) } @SetUp("ADD_SIMPLE") @Test fun assertSimpleMessage() { ChatPage(contact).assertMessageDisplayed(simpleMessage.text) } } 106
  • 107. SIMPLE TEXT ASSERTION BECOMES EASY @SetUp("ADD_SIMPLE") @Test fun assertSimpleMessage() { ChatPage(contact) .assertMessageDisplayed(simpleMessage.text) } 107
  • 108. TEST DATA DEFINITION FOR EACH TEST SOLUTION: CUSTOM SETUP RULE + @SETUP & @TEARDOWN ANNOTATIONS class SomeTestClass { ... .addSetUp("ADD_SIMPLE") { Api.addChatMessage(contact.id, simpleMessage) } .addSetUp("ADD_LONG") { Api.addChatMessage(contact.id, longMessage) } @SetUp("ADD_SIMPLE") @Test fun assertBothMessagesDisplayed() {} } 108
  • 109. TEST DATA DEFINITION FOR EACH TEST SOLUTION: CUSTOM SETUP RULE + @SETUP & @TEARDOWN ANNOTATIONS class SomeTestClass { ... .addSetUp("ADD_SIMPLE") { Api.addChatMessage(contact.id, simpleMessage) } .addSetUp("ADD_LONG") { Api.addChatMessage(contact.id, longMessage) } @SetUp("ADD_SIMPLE", "ADD_LONG") @Test fun assertBothMessagesDisplayed() {} } 109
  • 110. TEST DATA DEFINITION FOR EACH TEST SOLUTION: CUSTOM SETUP RULE + @SETUP & @TEARDOWN ANNOTATIONS class SomeTestClass { ... .addSetUp("ADD_SIMPLE") { Api.addChatMessage(contact.id, simpleMessage) } .addSetUp("ADD_LONG") { Api.addChatMessage(contact.id, longMessage) } @SetUp("ADD_SIMPLE", "ADD_LONG") @Test fun assertBothMessagesDisplayed() { ChatPage(contact) .assertMessageDisplayed(simpleMessage.text) .assertMessageDisplayed(longMessage.text) } 110
  • 112. 112
  • 113. MISTAKE TEST DATA IS RELATED WITH BUILD.SERIAL Tests runner server 113
  • 114. TEST DATA IS RELATED WITH BUILD.SERIAL class SomeTestClass { ... val user = TestData.getUser(Build.Serial) } 114
  • 115. TEST DATA IS RELATED WITH BUILD.SERIAL Tests runner server 115
  • 116. TEST DATA IS RELATED WITH BUILD.SERIAL USER 1 Tests runner server 116
  • 117. TEST DATA IS RELATED WITH BUILD.SERIAL USER 1 USER 2 USER 3 USER 4 USER 5 Tests runner server 117
  • 118. TEST DATA IS RELATED WITH BUILD.SERIAL USER 1 USER 2 USER 3 USER 4 USER 5 Tests runner server 118
  • 119. TEST DATA IS RELATED WITH BUILD.SERIAL USER 1 USER 2 USER 3 USER 4 USER 5 USER 6 Tests runner server 119
  • 120. TEST DATA IS RELATED WITH BUILD.SERIAL https://openstf.io/ 120
  • 121. TEST DATA IS RELATED WITH BUILD.SERIAL IF WE WANNA ADD OR CHANGE A DEVICE 121
  • 122. TEST DATA IS RELATED WITH BUILD.SERIAL IF WE WANNA ADD OR CHANGE A DEVICE ▸ Support changing in test code 122
  • 123. TEST DATA IS RELATED WITH BUILD.SERIAL IF WE WANNA ADD OR CHANGE A DEVICE ▸ Support changing in test code ▸ Configure test user to default test conditions 123
  • 124. TEST DATA IS RELATED WITH BUILD.SERIAL Tests runner server 124
  • 125. TEST DATA IS RELATED WITH BUILD.SERIAL Tests runner server Test data server 125
  • 126. TEST DATA IS RELATED WITH BUILD.SERIAL Tests runner server Test data server 126
  • 127. TEST DATA IS RELATED WITH BUILD.SERIAL ANY USER Tests runner server Test data server ANY USER ANY USER ANY USER ANY USER ANY USER 127
  • 128. WHAT TEST DATA SERVER PROVIDES TO US Test data server 128
  • 129. ▸ Pool of test users and any other required objects WHAT TEST DATA SERVER PROVIDES TO US Test data server 129
  • 130. ▸ Pool of test users and any other required objects ▸ Data management via rest api WHAT TEST DATA SERVER PROVIDES TO US Test data server 130
  • 131. ▸ Pool of test users and any other required objects ▸ Data management via rest api ▸ Guarantee of test data uniqueness for separate test WHAT TEST DATA SERVER PROVIDES TO US Test data server 131
  • 132. ▸ Kotlin ▸ Spring Boot (https://spring.io/projects/spring-boot) ▸ JetBrains Exposed (https://github.com/JetBrains/Exposed) WHAT TEST DATA SERVER FORMULA Test data server 132
  • 133. SOLUTION: TEST DATA SERVER TEST DATA IS RELATED WITH BUILD.SERIAL 133
  • 134. SOLUTION: TEST DATA SERVER ▸ No need to support devices TEST DATA IS RELATED WITH BUILD.SERIAL 134
  • 135. SOLUTION: TEST DATA SERVER ▸ No need to support devices ▸ Single point to control all test data TEST DATA IS RELATED WITH BUILD.SERIAL 135
  • 136. SOLUTION: TEST DATA SERVER ▸ No need to support devices ▸ Single point to control all test data ▸ Multiple test runs at the same time (CI tasks) TEST DATA IS RELATED WITH BUILD.SERIAL 136
  • 137. SOLUTION: TEST DATA SERVER ▸ No need to support devices ▸ Single point to control all test data ▸ Multiple test runs at the same time (CI tasks) ▸ It could be used by other test frameworks (iOS, Web, api tests) TEST DATA IS RELATED WITH BUILD.SERIAL 137
  • 138. TEST DATA USAGE ▸ Test data is used everywhere:
 -> Strict data definition to test suite or test case
 -> Store read-only data as constants 138
  • 139. TEST DATA USAGE ▸ Test data is used everywhere:
 -> Strict data definition to test suite or test case
 -> Store read-only data as constants ▸ Test data is defined after activity launch
 -> Use custom SetUpTearDown rule + RuleSequence 139
  • 140. TEST DATA USAGE ▸ Test data is used everywhere:
 -> Strict data definition to test suite or test case
 -> Store read-only data as constants ▸ Test data is defined after activity launch
 -> Use custom SetUpTearDown rule + RuleSequence ▸ Test data is defined in @BeforeClass or @Before method for all @Test methods
 -> Use SetUpTearDown rule + SetUp and TearDown annotations 140
  • 141. TEST DATA USAGE ▸ Test data is used everywhere:
 -> Strict data definition to test suite or test case
 -> Store read-only data as constants ▸ Test data is defined after activity launch
 -> Use custom SetUpTearDown rule + RuleSequence ▸ Test data is defined in @BeforeClass or @Before method for all @Test methods
 -> Use SetUpTearDown rule + SetUp and TearDown annotations ▸ Test user is related with device Build.Serial
 -> Use TestData server 141
  • 148. DEMO
  • 149. 149
  • 150. PREVIOUSLY ON HEISENBUG COOL TEST @Test fun goodTest(){ FriendsListPage().openChat("Ross Geller") ChatPage().clearHistory() .sendMessage("test message") } 150
  • 151. IF I WANNA SEND ANOTHER MESSAGE IS IT A GOOD IDEA? @Test fun sendMessageWithLink(){ FriendsListPage().openChat("Ross Geller”) ChatPage().clearHistory() .sendMessage("message with link vk.com") } 151
  • 152. IF I WANNA SEND ANOTHER MESSAGE ACTUALLY IT ISN’T A GOOD IDEA! @Test fun sendMessageWithLink(){ FriendsListPage().openChat("Ross Geller”) ChatPage().clearHistory() .sendMessage("message with link vk.com") } 152
  • 154. MISTAKE LONG TEST SCENARIO ▸ It’s a flaky way 154
  • 155. MISTAKE LONG TEST SCENARIO ▸ It’s a flaky way ▸ It takes a lot of time 155
  • 156. MISTAKE LONG TEST SCENARIO ▸ It’s a flaky way ▸ It takes a lot of time ▸ Code duplication 156
  • 157. STOP WRITING LONG TESTS MARTY, LOOK AT THE SHORT TEST! 157
  • 158. LONG TEST SCENARIO SOLUTION: SHORT TEST @Test fun sendMessageWithLink(){ FriendsListPage().openChat("Ross Geller”) ChatPage().clearHistory() .sendMessage("message with link vk.com") } 158
  • 159. LONG TEST SCENARIO SOLUTION: SHORT TEST @Test fun sendMessageWithLink(){ FriendsListPage().openChat("Ross Geller”) ChatPage().clearHistory() ChatPage().sendMessage("message with link vk.com") } 159
  • 160. LONG TEST SCENARIO SOLUTION: SHORT TEST class ChatTest { @Test fun sendMessageWithLink() { ChatPage().sendMessage("message with link vk.com") } } 160
  • 161. SHORT TEST: LAUNCH TARGET ACTIVITY class ChatTest { @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java, false, false) } 161
  • 162. SHORT TEST: LAUNCH TARGET ACTIVITY class ChatTest { @get:Rule val activityRule = ActivityTestRule(MainActivity::class.java, false, false) } 162
  • 163. class ChatTest { @get:Rule val activityRule = ActivityTestRule(ChatActivity::class.java, false, false) } SHORT TEST: LAUNCH TARGET ACTIVITY 163
  • 164. SHORT TEST: LAUNCH TARGET ACTIVITY class ChatTest { @get:Rule val activityRule = ActivityTestRule(ChatActivity::class.java, false, false) @Before fun prepareData() { MessageRepository.clearChatMessages(contact.id) val intent = Intent() intent.putExtra("INTENT_CONTACT_ID_EXTRA_NAME", contact.id) activityRule.launchActivity(intent) } } 164
  • 165. SHORT TEST: LAUNCH TARGET ACTIVITY class ChatTest { @get:Rule val activityRule = ActivityTestRule(ChatActivity::class.java, false, false) @Before fun prepareData() { MessageRepository.clearChatMessages(contact.id) val intent = Intent() intent.putExtra("INTENT_CONTACT_ID_EXTRA_NAME", contact.id) activityRule.launchActivity(intent) } } 165
  • 166. SHORT TEST: LAUNCH TARGET ACTIVITY class ChatTest { @get:Rule val activityRule = ActivityTestRule(ChatActivity::class.java, false, false) @Before fun prepareData() { MessageRepository.clearChatMessages(contact.id) val intent = Intent() intent.putExtra("INTENT_CONTACT_ID_EXTRA_NAME", contact.id) activityRule.launchActivity(intent) } } 166
  • 167. SHORT TEST: LAUNCH TARGET ACTIVITY class ChatTest { @get:Rule val activityRule = ActivityTestRule(ChatActivity::class.java, false, false) @Before fun prepareData() { MessageRepository.clearChatMessages(contact.id) val intent = Intent() intent.putExtra("INTENT_CONTACT_ID_EXTRA_NAME", contact.id) activityRule.launchActivity(intent) } } 167
  • 168. SHORT TEST: LAUNCH TARGET ACTIVITY class ChatTest { @get:Rule val activityRule = ActivityTestRule(ChatActivity::class.java, false, false) @Before fun prepareData() { MessageRepository.clearChatMessages(contact.id) val intent = Intent() intent.putExtra("INTENT_CONTACT_ID_EXTRA_NAME", contact.id) activityRule.launchActivity(intent) } } 168
  • 169. SHORT TEST: LAUNCH TARGET ACTIVITY class ChatTest { @get:Rule val activityRule = ActivityTestRule(ChatActivity::class.java, false, false) @Before fun prepareData() { MessageRepository.clearChatMessages(contact.id) val intent = Intent() intent.putExtra("INTENT_CONTACT_ID_EXTRA_NAME", contact.id) activityRule.launchActivity(intent) } @Test fun sendMessageWithLink() { ChatPage().sendMessage("message with link vk.com") } } 169
  • 170. SHORT TEST: LAUNCH TARGET ACTIVITY class ChatTest { @get:Rule val activityRule = ActivityTestRule(ChatActivity::class.java, false, false) @Before fun prepareData() { MessageRepository.clearChatMessages(contact.id) val intent = Intent() intent.putExtra("INTENT_CONTACT_ID_EXTRA_NAME", contact.id) activityRule.launchActivity(intent) } @Test fun sendMessageWithLink() {...} } 170
  • 171. SHORT TEST: LAUNCH TARGET ACTIVITY class ChatTest { @get:Rule val activityRule = ActivityTestRule(ChatActivity::class.java, false, false) val setupRule = SetUpTearDownRule() .addSetUp { MessageRepository.clearChatMessages(contact.id) val intent = Intent() intent.putExtra("INTENT_CONTACT_ID_EXTRA_NAME", contact.id) activityRule.launchActivity(intent) } @Test fun sendMessageWithLink() {...} } 171
  • 172. SHORT TEST: LAUNCH TARGET ACTIVITY class ChatTest { @get:Rule val activityRule = ActivityTestRule(ChatActivity::class.java, false, false) val setupRule = SetUpTearDownRule() .addSetUp { MessageRepository.clearChatMessages(contact.id) val intent = Intent() intent.putExtra("INTENT_CONTACT_ID_EXTRA_NAME", contact.id) activityRule.launchActivity(intent) } @Test fun sendMessageWithLink() {...} } 172
  • 173. SHORT TEST: LAUNCH TARGET ACTIVITY class ChatTest { val activityRule = ActivityTestRule(ChatActivity::class.java, false, false) val setupRule = SetUpTearDownRule() .addSetUp { MessageRepository.clearChatMessages(contact.id) val intent = Intent() intent.putExtra("INTENT_CONTACT_ID_EXTRA_NAME", contact.id) activityRule.launchActivity(intent) } @get:Rule val ruleSequence = RuleSequence(setupRule, activityRule) @Test fun sendMessageWithLink() {...} } 173
  • 175. SHORT TEST: LAUNCH TARGET ACTIVITY 175
  • 176. DEMO
  • 177. 177
  • 178. LONG TEST SCENARIO SOLUTION: SHORT TEST class ChatTest { @Test fun sendMessageWithLink() { ChatPage().sendMessage("message with link vk.com") } } 178
  • 179. BLACKLIST CASE WHAT CAN I DO WITH FRAGMENTS? 179
  • 185. SAME ACTIVITY BUT DIFFERENT FRAGMENTS 185
  • 186. BLACKLIST DATA PREPARATION class BlacklistTests { ... @get:Rule val ruleSequence = RuleSequence(setupRule, activityRule) } 186
  • 187. BLACKLIST DATA PREPARATION class BlacklistTests { ... @get:Rule val ruleSequence = RuleSequence(setupRule, activityRule) @Before fun openFragment(){ activityRule.runOnUiThread { SettingsFragmentNavigator.go(BlacklistFragment::class.java) } } } 187
  • 188. BLACKLIST DATA PREPARATION class BlacklistTests { ... @get:Rule val ruleSequence = RuleSequence(setupRule, activityRule) @Before fun openFragment(){ activityRule.runOnUiThread { SettingsFragmentNavigator.go(BlacklistFragment::class.java) } } } 188
  • 189. BLACKLIST DATA PREPARATION class BlacklistTests { ... @get:Rule val ruleSequence = RuleSequence(setupRule, activityRule) @Before fun openFragment(){ activityRule.runOnUiThread { SettingsFragmentNavigator.go(BlacklistFragment::class.java) } } } 189
  • 190. BLACKLIST DATA PREPARATION class BlacklistTests { ... @get:Rule val ruleSequence = RuleSequence(setupRule, activityRule) .addLast(SetUpTearDownRule().addSetUp { activityRule.runOnUiThread { SettingsFragmentNavigator.go(BlacklistFragment::class.java) } }) } 190
  • 198. BLACKLIST DATA PREPARATION class BlacklistTests { val activityRule = ActivityTestRule(SettingsActivity::class.java) val setUpTearDownRule = SetUpTearDownRule() .addSetUp { ContactsRepositoty.clearBlacklist() ContactsRepositoty.addToBlacklist(contact.id) } @get:Rule val ruleSequence = RuleSequence(setupRule, activityRule) } 198
  • 199. BLACKLIST DATA PREPARATION class BlacklistTests { val activityRule = ActivityTestRule(SettingsActivity::class.java) val setUpTearDownRule = SetUpTearDownRule() .addSetUp { ContactsRepositoty.clearBlacklist() ContactsRepositoty.addToBlacklist(contact.id) } @get:Rule val ruleSequence = RuleSequence(setupRule, activityRule) } 199
  • 200. BLACKLIST DATA PREPARATION class BlacklistTests { val activityRule = ActivityTestRule(SettingsActivity::class.java) val setUpTearDownRule = SetUpTearDownRule() .addSetUp { ContactsRepositoty.clearBlacklist() ContactsRepositoty.addToBlacklist(contact.id) } @get:Rule val ruleSequence = RuleSequence(setupRule, activityRule) } 200
  • 203. DEMO
  • 204. 204
  • 205. SOLUTION: USE INTERNAL NAVIGATION MECHANISM FRAGMENTS NAVIGATION PROBLEM 205 activityRule.runOnUiThread { SettingsFragmentNavigator.go(BlacklistFragment::class.java) }
  • 206. LOGIN WHAT CAN I DO WITH LOGIN PROBLEM? 206
  • 207. LOGIN WHAT CAN I DO WITH LOGIN PROBLEM? ▸ Use authorisation via UI 207
  • 208. LOGIN WHAT CAN I DO WITH LOGIN PROBLEM? ▸ Use authorisation via UI ▸ Use internal authorisation mechanism 208
  • 211. USE AUTHORISATION VIA UI USE INTERNAL AUTHORISATION MECHANISM AccountManager() .login( user.login, user.password ) 211
  • 212. INTERNAL AUTHORISATION MECHANISM class ChatTest { ... val setupRule = SetUpTearDownRule() .addSetUp { MessageRepository.clearChatMessages(contact.id) val intent = Intent() intent.putExtra("INTENT_CONTACT_ID_EXTRA_NAME", contact.id) activityRule.launchActivity(intent) } @Test fun sendMessageWithLink() {... } } 212
  • 213. INTERNAL AUTHORISATION MECHANISM class ChatTest { ... val setupRule = SetUpTearDownRule() .addSetUp { MessageRepository.clearChatMessages(contact.id) ... } @Test fun sendMessageWithLink() {... } } 213
  • 214. INTERNAL AUTHORISATION MECHANISM class ChatTest { … val setupRule = SetUpTearDownRule() .addSetUp { AccountManager(targetContext).login(user.login, user.password) MessageRepository.clearChatMessages(contact.id) ... } @Test fun sendMessageWithLink() {... } } 214
  • 215. SOLUTION: USE INTERNAL AUTHORISATION MECHANISM LOGIN PROBLEM 215 AccountManager(targetContext).login(user.login, user.password)
  • 216. TEST FLOW I HAD THE LONGEST TEST EVER BUT NOW … 216
  • 217. TEST FLOW I HAD THE LONGEST TEST EVER BUT NOW … ▸ Long test scenario 217
  • 218. TEST FLOW I HAD THE LONGEST TEST EVER BUT NOW … ▸ Long test scenario 
 -> Use short tests by opening target activity 218
  • 219. TEST FLOW I HAD THE LONGEST TEST EVER BUT NOW … ▸ Long test scenario 
 -> Use short tests by opening target activity ▸ I navigate my tests to the Target Screen by UI actions 
 219
  • 220. TEST FLOW I HAD THE LONGEST TEST EVER BUT NOW … ▸ Long test scenario 
 -> Use short tests by opening target activity ▸ I navigate my tests to the Target Screen by UI actions 
 -> Use internal navigation mechanism 220
  • 221. TEST FLOW I HAD THE LONGEST TEST EVER BUT NOW … ▸ Long test scenario 
 -> Use short tests by opening target activity ▸ I navigate my tests to the Target Screen by UI actions
 -> Use internal navigation mechanism ▸ Login by UI actions 
 221
  • 222. TEST FLOW I HAD THE LONGEST TEST EVER BUT NOW … ▸ Long test scenario 
 -> Use short tests by opening target activity ▸ I navigate my tests to the Target Screen by UI actions 
 -> Use internal navigation mechanism ▸ Login by UI actions 
 -> Do it by internal authorisation mechanism 222
  • 223. TEST FLOW ▸ Long test scenario 
 -> Use short tests by opening target activity ▸ I navigate my tests to the Target Screen by UI actions 
 -> Use internal navigation mechanism ▸ Login by UI actions 
 -> Do it by internal authorisation mechanism 223 I HAD THE LONGEST TEST EVER BUT NOW I HAVE THE SHORTEST ONE
  • 225. TEST FRAMEWORK ARCHITECTURE MY FIRST TEST FRAMEWORK 225
  • 226. 226
  • 227. TEST SUITES TEST CLASSES TEST DATA TEST STEPSASSERTIONS TEST ADAPTERS 227
  • 228. TEST SUITES TEST CLASSES TEST DATA TEST STEPSASSERTIONS TEST ADAPTERS 228
  • 229. TEST SUITES TEST CLASSES TEST DATA TEST STEPSASSERTIONS TEST ADAPTERS STEPS 229
  • 230. TEST SUITES TEST CLASSES TEST DATA TEST STEPSASSERTIONS TEST ADAPTERS APP UI ELEMENTS STEPS 230
  • 231. TEST SUITES TEST CLASSES TEST DATA TEST STEPSASSERTIONS TEST ADAPTERS APP UI ELEMENTS STEPS PAGE OBJECT CLASS 231
  • 232. TEST FRAMEWORK ARCHITECTURE MY FIRST TEST FRAMEWORK 232
  • 233. TEST FRAMEWORK ARCHITECTURE MY CURRENT TEST FRAMEWORK 233
  • 234. MAKING YOUR OWN REPORT 234
  • 235. REPORTING IT WILL LOOKS LIKE THIS 235
  • 236. 236 BUT IT LOOKS LIKE THIS
  • 239. REPORTING WHY IT HAPPENS? ▸ Support hell 239
  • 240. REPORTING WHY IT HAPPENS? ▸ Support hell ▸ Integration with CI hell 240
  • 241. REPORTING SOLUTION: USE ALREADY MADE LIBS & TOOLS 241
  • 242. REPORTING SOLUTION: USE ALREADY MADE LIBS & TOOLS ▸ Allure android 242
  • 243. REPORTING SOLUTION: USE ALREADY MADE LIBS & TOOLS ▸ Allure android ▸ Test runner report 243
  • 244. REPORTING SOLUTION: USE ALREADY MADE LIBS & TOOLS ▸ Allure android ▸ Test runner report ▸ Another already made report 244
  • 245. TYPES OF MISTAKES WE’VE DISCOVERED BACK TO THE PAST 245
  • 246. TYPES OF MISTAKES WE’VE DISCOVERED BACK TO THE PAST ▸ Test data usage 246
  • 247. TEST DATA USAGE ▸ Test data is used everywhere:
 -> Strict data definition to test suite or test case
 -> Store read-only data as constants ▸ Test data is defined after activity launch
 -> Use custom SetUpTearDown rule ▸ Test data is defined in @BeforeClass or @Before method for all @Test methods
 -> Use SetUpTearDown rule + SetUp and TearDown annotations ▸ Test user is related with device Build.Serial
 -> Use TestData server 247
  • 248. TYPES OF MISTAKES WE’VE DISCOVERED BACK TO THE PAST ▸ Test data usage ▸ Test flow 248
  • 249. TEST FLOW ▸ Long test scenario 
 -> Use short tests by opening target activity ▸ I navigate my tests to the Target Screen by UI actions 
 -> Use internal navigation mechanism ▸ Login by UI actions 
 -> Do it by internal authorisation mechanism 249 I HAD THE LONGEST TEST EVER BUT NOW I HAVE THE SHORTEST ONE
  • 250. TYPES OF MISTAKES WE’VE DISCOVERED BACK TO THE PAST ▸ Test data usage ▸ Test flow ▸ Framework architecture 250
  • 251. 251SOLUTION: USE COMMON PRACTICES AND ADAPT IT TEST SUITES TEST CLASSES TEST DATA TEST STEPSASSERTIONS TEST ADAPTERS APP UI ELEMENTS STEPS PAGE OBJECT CLASS
  • 252. TYPES OF MISTAKES WE’VE DISCOVERED BACK TO THE PAST ▸ Test data usage ▸ Test flow ▸ Framework architecture ▸ Reporting 252
  • 253. REPORTING SOLUTION: USE ALREADY MADE LIBS & TOOLS ▸ Allure android ▸ Test runner report ▸ Another already made report 253
  • 254. GitHub: 1) Примеры кода github.com/alex-tiurin/espresso-guide 2) Библиотека c Espresso DSL github.com/alex-tiurin/espresso-page-object Как подключить себе в проект? repositories { jcenter() } dependencies { //espresso-page-object androidTestImplementation 'com.atiurin.espresso:espressopageobject:0.1.16' }