Skip to content

Commit a977bae

Browse files
committed
Testing minimized build docs
1 parent 8e84a9f commit a977bae

File tree

2 files changed

+179
-0
lines changed

2 files changed

+179
-0
lines changed
+178
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
# Testing obfuscated build
2+
3+
How to test the closest possible to production build.
4+
5+
## Problem
6+
7+
We work on debug builds and most often see debug builds and our UI tests run on debug builds. But user has release build. It is the same build apart from optimisations we do to reduce binary size and protect our apps identity. Any problems related to these optimisations are very rare but still would be good to catch them even before they hit beta.
8+
9+
## Solution
10+
11+
Run UI tests on obfuscated build. For that we need to use [keeper](https://slackhq.github.io/keeper/) plugin. The reason is that Android Gradle Plugin doesn't include usages from `androidTest` sources and will throw out all code referenced by UI tests. So they won't work.
12+
13+
We can use Keeper in two ways:
14+
15+
### Run UI tests on release build
16+
17+
Can be done by adding following to `build.gradle`:
18+
19+
```
20+
android {
21+
testBuildType = "release"
22+
}
23+
```
24+
or rather:
25+
```
26+
android {
27+
if (hasProperty("testingMinimizedBuild")) {
28+
testBuildType = "release"
29+
}
30+
}
31+
```
32+
33+
But this has downside: sometimes you separate code via build type folders. E.g. place dummy implementation in `/debug/` and real implementation into `/release/` source set to make sure debug code never gets into production builds. Or the same principle applied to dependencies like:
34+
35+
```
36+
debugImplementation 'com.facebook.flipper:flipper:0.154.0'
37+
debugImplementation 'com.facebook.soloader:soloader:0.10.1'
38+
releaseImplementation 'com.facebook.flipper:flipper-noop:0.154.0'
39+
```
40+
41+
If you have such configurations, it's not a way to go.
42+
43+
### Run obfuscation on debug build.
44+
45+
Instead of running tests on release build type, we run them as usual on debug, but apply obfuscation to debug build via:
46+
47+
```
48+
buildTypes {
49+
debug {
50+
...
51+
if (hasProperty("testingMinimizedBuild")) {
52+
isMinifyEnabled = hasProperty("testingMinimizedBuild")
53+
isShrinkResources = hasProperty("testingMinimizedBuild")
54+
proguardFiles 'proguard-rules.pro'
55+
}
56+
}
57+
}
58+
```
59+
60+
### Main trick
61+
62+
Once we have build and minimize for tests we need to keep all needed classes. To do so we apply [keeper](https://slackhq.github.io/keeper/) plugin:
63+
64+
```
65+
if (hasProperty("testingMinimizedBuild")) {
66+
apply plugin: "com.slack.keeper"
67+
}
68+
```
69+
70+
As you noted we do everything if some property(eg `hasProperty("testingMinimizedBuild")`). This way we can run UI tests normally and to run tests on obfuscated build.
71+
72+
To pass param to the build:
73+
74+
```
75+
./gradlew assembleDebugAndroidTest -PtestingMinimizedBuild
76+
```
77+
78+
### Other tricks
79+
80+
#### R8 repo
81+
82+
Keeper adds R8 repo on project level so if your project uses
83+
84+
```
85+
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
86+
```
87+
88+
it will fail the build. What you need to do is to tell keeper not to add any repos and do it yourself:
89+
90+
```
91+
keeper {
92+
automaticR8RepoManagement = false
93+
}
94+
...
95+
repositories {
96+
...
97+
maven { setUrl("http://storage.googleapis.com/r8-releases/raw") }
98+
}
99+
```
100+
101+
#### Memory and time
102+
103+
Obviously build will take longer time depending on project size. But you also need to increase heap memory for JVM, otherwise you'll get lots OOMs.
104+
105+
You can either do it in gradle.properties file:
106+
```
107+
org.gradle.jvmargs=-Xmx16G -XX:+UseParallelGC -Dfile.encoding=UTF-8
108+
```
109+
110+
Or to give more memory only for those runs(your final command may look like):
111+
112+
```
113+
./gradlew assembleDebugAndroidTest -PtestingMinimizedBuild "-Dorg.gradle.jvmargs=-Xmx16G -XX:+UseParallelGC" -Dfile.encoding=UTF-8
114+
```
115+
116+
*Note*: double quotes for `"-Dorg.gradle.jvmargs=-Xmx16G -XX:+UseParallelGC"` otherwise gradle may be unhappy with incorrect `org.gradle.jvmargs`.
117+
118+
#### Additional Proguard rules
119+
120+
Depending on your UI tests you may want to disable obfuscation of certain classes in addition to you main Proguard rules so that your test code can find needed stuff.
121+
122+
`proguard-debug-r8.pro`
123+
124+
```
125+
# Make UI tests able to find needed stuff.
126+
-keep class org.yaml.** { *; }
127+
-keep class okreplay.** { *; }
128+
-keepattributes InnerClasses
129+
-keep class **.R
130+
-keep class **.R$* {
131+
<fields>;
132+
}
133+
```
134+
135+
And in:
136+
137+
```
138+
buildTypes {
139+
release {
140+
minifyEnabled true
141+
proguardFiles 'proguard-rules.pro'
142+
}
143+
release {
144+
if (hasProperty("testingMinimizedBuild")) {
145+
minifyEnabled true
146+
proguardFiles 'proguard-rules.pro', 'proguard-debug-r8.pro' // here we extend proguard with our test specific rules file
147+
}
148+
}
149+
}
150+
```
151+
152+
#### AGP version
153+
154+
If your Android Gradle Plugin version is less than `7.1.0` than you need not the latest version of keeper. You need `0.11.2`.
155+
156+
This is because of new gradle API through which you apply the plugin.
157+
158+
Also on different versions of AGP work different R8. If something doesn't work(you see some `PrintUses` stack trace) you may want to try new R8 `TraceReferences` API(worked for us on AGP 7.1.+):
159+
160+
```
161+
keeper {
162+
traceReferences()
163+
}
164+
```
165+
166+
Otherwise you may want to try different version of R8. Look for tags [here](https://r8.googlesource.com/r8/). More [here](https://slackhq.github.io/keeper/configuration/#custom-r8-behavior).
167+
168+
169+
### Further reading:
170+
171+
1. [Keeper advanced configuration](https://slackhq.github.io/keeper/configuration/) and reading source code.
172+
2. [Testing minimized build at Avito](https://avito-tech.github.io/avito-android/blog/2020/03/testing-a-minimized-build/)
173+
174+
175+
176+
177+
178+

mkdocs.yml

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ nav:
5555
- Flakiness: practices/flakiness.md
5656
- Page object: practices/page_object.md
5757
- Sharing ui components among tests: practices/shared_test_components.md
58+
- Testing obfuscated build: practices/obfuscated_build.md
5859
- Adoption:
5960
#- Where to begin: adoption/where_to_begin.md
6061
- Companies experience: adoption/companies_experience.md

0 commit comments

Comments
 (0)