1bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin/*
2bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin * Copyright (C) 2015 DroidDriver committers
3bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *
4bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin * Licensed under the Apache License, Version 2.0 (the "License");
5bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin * you may not use this file except in compliance with the License.
6bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin * You may obtain a copy of the License at
7bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *
8bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *      http://www.apache.org/licenses/LICENSE-2.0
9bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *
10bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin * Unless required by applicable law or agreed to in writing, software
11bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin * distributed under the License is distributed on an "AS IS" BASIS,
12bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin * See the License for the specific language governing permissions and
14bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin * limitations under the License.
15bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin */
16bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin
17bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin/**
18bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin * Helper classes for writing an Android UI test framework using DroidDriver.
19bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *
20bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin * <h2>UI test framework design principles</h2>
21bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *
22bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin * A UI test framework should model the UI of the AUT in a hierarchical way to maximize code reuse.
23bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin * Common interactions should be abstracted as methods of page objects. Uncommon interactions may
24bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin * not be abstracted, but carried out using "driver" directly.
259d965bfdd63fdaeb8683e75dfb17f9da864a6028bootstraponline * <p>
26bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin * The organization of the entities (pages, components) does not need to strictly follow the AUT
27bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin * structure. The UI model can be greatly simplified to make it easy to use.
289d965bfdd63fdaeb8683e75dfb17f9da864a6028bootstraponline * <p>
29bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin * In general the framework should follow these principles:
30bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin * <ul>
31bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *   <li>Layered abstraction: at the highest level, methods completely abstract the implementation
32bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       detail. This kind of methods carry out a complex action, usually involving multiple steps.
33bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       At a lower level, methods can expose some details, e.g. clickInstallButton(), which does a
34bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       single action and returns a dialog instance it opens, and let the caller decide how to
35bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       further interact with it. Lastly at the lowest level, you can always use "driver" to access
36bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       any elements if no higher-level methods are available.</li>
37bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *   <li>Instance methods of a page object assume the page is currently shown.</li>
38bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *   <li>If a method opens another page, it should return that page on a best-effort basis. There
39bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       could be exceptions where we let callers determine the type of the new page, but that
40bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       should be considered hacks and be clearly documented.</li>
41bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *   <li>The page object constructors are public so that it's easy to hack as mentioned above, but
42bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       don't abuse it -- typically callers should acquire page objects by calling methods of other
43bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       page objects. The root is the home page of the AUT.</li>
44bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *   <li>Simple dialogs may not merit their own top-level classes, and can be nested as static
45bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       subclasses.</li>
46bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *   <li>Define constants that use values generated from Android resources instead of using string
47bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       literals. For example, call {@link android.content.Context#getResources} to get the
48bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       Resources instance, then call {@link android.content.res.Resources#getResourceName} to get
49bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       the string representation of a resource id, or call {@link
50bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       android.content.res.Resources#getString} to get the localized string of a string resource.
51bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       This gives you compile-time check over incompatible changes.</li>
52bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *   <li>Avoid public constants. Typically clients of a page object are interested in what can be
53bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       done on the page (the content or actions), not how to achieve that (which is an
54bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       implementation detail). The constants used by the page object hence should be encapsulated
55bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       (declared private). Another reason for this item is that the constants may not be real
56bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       constants. Instead they are generated from resources and acquiring the values requires the
57bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       {@link android.content.Context}, which is not available until setUp() is called. If those
58bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       are referenced in static fields of a test class, they will be initialized at class loading
59bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       time and result in a crash.</li>
60bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *   <li>There are cases that exposing public constants is arguably desired. For example, when the
61bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       interaction is trivial (e.g. clicking a button that does not open a new page), and there
62bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       are many similar elements on the page, thus adding distinct methods for them will bloat the
63bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       page object class. In these cases you may define public constants, with a warning that
64bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       "Don't use them in static fields of tests".</li>
65bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin * </ul>
66bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *
67bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin * <h2>Common pitfalls</h2>
68bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin * <ul>
69bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *   <li>UI elements are generally views. Users can get attributes and perform actions. Note that
70bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       actions often update a UiElement, so users are advised not to store instances of UiElement
71bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       for later use - the instances could become stale. In other words, UiElement represents a
72bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       dynamic object, while Finder represents a static object. Don't declare fields of the type
73bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       UiElement; use Finder instead.</li>
74bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *   <li>{@link android.test.ActivityInstrumentationTestCase2#getActivity} calls
75bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       {@link android.test.InstrumentationTestCase#launchActivityWithIntent}, which may hang in
76bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       {@link android.app.Instrumentation#waitForIdleSync}. You can call
77bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       {@link android.content.Context#startActivity} directly.</li>
78bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *   <li>startActivity does not wait until the new Activity is shown. This may cause problem when
79bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       the old Activity on screen contains UiElements that match what are expected on the new
80bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       Activity - interaction with the UiElements fails because the old Activity is closing.
81bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       Sometimes it shows as a test passes when run alone but fails when run with other tests.
82bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       The work-around is to add a delay after calling startActivity.</li>
83bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *   <li>Error "android.content.res.Resources$NotFoundException: Unable to find resource ID ..."?
849d965bfdd63fdaeb8683e75dfb17f9da864a6028bootstraponline *       <br>
85bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       This may occur if you reference the AUT's resource in tests, and the two APKs are out of
86bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       sync. Solution: build and install both AUT and tests together.</li>
87bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *   <li>"You said the test runs on older devices as well as API18 devices, but mine is broken on
88bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       X (e.g. GingerBread)!"
899d965bfdd63fdaeb8683e75dfb17f9da864a6028bootstraponline *       <br>
90bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       This may occur if your AUT has different implementations on older devices. In this case,
91bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       your tests have to match the different execution paths of AUT, which requires insight into
92bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       the implementation of the AUT. A tip for testing older devices: uiautomatorviewer does not
93bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       work on ore-API16 devices (the "Device screenshot" button won't work), but you can use it
94bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       with dumps from DroidDriver (use to-uiautomator.xsl to convert the format).</li>
95bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *   <li>"com.android.launcher has stopped unexpectedly" and logcat says OutOfMemoryError
969d965bfdd63fdaeb8683e75dfb17f9da864a6028bootstraponline *       <br>
97bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       This is sometimes seen on GingerBread or other low-memory and slow devices. GC is not fast
98bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       enough to reclaim memory on those devices. A work-around: call gc more aggressively and
99bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       sleep to let gc run, e.g.
100bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin *       <pre>
101bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jinpublic void setUp() throws Exception {
102bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin  super.setUp();
1039d965bfdd63fdaeb8683e75dfb17f9da864a6028bootstraponline  if (Build.VERSION.SDK_INT &lt;= Build.VERSION_CODES.GINGERBREAD_MR1) {
104bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin    Runtime.getRuntime().gc();
105bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin    SystemClock.sleep(1000L);
106bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin  }
107bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin}
108bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin</pre></li>
109bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin * </ul>
110bcb11b2a88201ef6365180b33703466bfee7d9c4Kevin Jin */
1114b31201b5a2dbf8036da5a8d089a68a39cc1dc44Kevin Jinpackage io.appium.droiddriver.helpers;
112