An overview of the
In order to avoid repeated artificial regression tests before each launch and ensure that each launch version will not cause instability of the core business, automated testing is urgently needed to ensure the stability of the business. After research, I tried to use Appium for automated testing, because it is powerful, cross-platform and has an active community.
Comparison of mainstream Frameworks
Appium advantages
- Open source
- Cross-architecture :Native App, Hybird App, Web App
- Cross-device :Android, iOS, and Firefox OS
- Independent of source code
- Use any WebDriver compatible language to write test cases. Java, Objective-C, JavaScript with Node.js (in both callback and yield-based flavours), PHP, Python, Ruby, C#, Clojure, or Perl.
- You don’t need to recompile your APP
For those who are not aware of WebDriver, this will be explained in the Appium architecture introduction.
Appium concept
- You don’t have to recompile or modify your application for automation.
- You don’t have to be limited to a language or framework to write and run test scripts.
- A mobile automation framework should not duplicate wheels on interfaces. (Mobile automation interface should be unified)
- It must be open source in spirit and name.
Appium architecture
IOS: Apple UIAutomation Android 4.2+: Google UiAutomator Android 2.3+: Google’s Instrumentation. (supported by a separate project Selendroid)
Appium 1.6 + has been addedUiAutomator2
To meet the cross-platform requirements above, these tripartite frameworks are packaged into a set of apis — the WebDriver API (client-to-server protocol)
In fact, WebDriver has become a standard for Web browser automation and a W3C standard – W3C Working Draft, so Appium extends the mobile automation related API on the original basis.
Investing in WebDriver means you can bet on an independent, free and open protocol that has become standard. You are not limited by any patent.
Core architecture: Appium uses C/S architecture. At runtime, the Service listens for commands sent by the Client, executes these commands on the mobile device, and returns the execution results to the Client in an HTTP response.
What can be done based on this architecture?
- You can write the test code in any language that implements the client
- You can put the server on a different machine
- You can just write the test code and use a cloud service like Saucelabs to interpret the commands.
The following diagram illustrates the specific role of cloud services:
Appium use
The service side
-
Install the Appium server
npm install -g appium npm install -g appium-doctor appium-doctor Copy the code
Among them, appium-doctor is used to check whether the computer lacks relevant dependencies. When all check boxes are checked, the Appium environment is configured as follows:
-
Start appium server:
Appium --address 127.0.0.1 --port 4723 --log "/Users/mio4kon/Desktop/ appium.log" --log-timestamp --local-timezone --session-overrideCopy the code
The client
Again, Appium supports a variety of languages, but I chose JAVA. If JAVA syntax is not concise or familiar, use a language you are familiar with.
Create MAVEN/Gradle project:
Create the project and add the following dependencies:
<dependency>
<groupId>io.appium</groupId>
<artifactId>java-client</artifactId>
<version>5.0.0 beta 2 -</version>
<exclusions>
<exclusion>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.0.1</version>
</dependency>
Copy the code
The dependency on the Appium client is introduced successfully.
Capabiltiy configuration:
The setUp method is defined in the base class of each Test and other initialization operations are set before Capabiltiy:
DesiredCapabilities capabilities = new DesiredCapabilities ();
capabilities.setCapability (MobileCapabilityType.DEVICE_NAME, deviceName);
capabilities.setCapability (MobileCapabilityType.PLATFORM_NAME, platformName);
capabilities.setCapability (MobileCapabilityType.PLATFORM_VERSION, platformVersion);
capabilities.setCapability (MobileCapabilityType.APP, apkPath);
capabilities.setCapability (AndroidMobileCapabilityType.APP_PACKAGE, appPackage);
capabilities.setCapability (AndroidMobileCapabilityType.APP_ACTIVITY, appActivity);
capabilities.setCapability (MobileCapabilityType.AUTOMATION_NAME, AutomationName.ANDROID_UIAUTOMATOR2);
Copy the code
Here I’m using the Parameters annotation in TestNG to configure the Parameters.
What the hell is TestNG? TestNG is a Java testing framework, similar to JUnit but more powerful and easy to use.
TestNG
Prepare and close with some comments from TestNg.
I used the BeforeClass and Parameters annotations for the setUp method above.
@BeforeClass
@Parameters({"driverName"."url"."deviceName"."platformName"."platformVersion"."apkPath"."appPackage"."appActivity"})
public void setUp(String driverName, String url, String deviceName, String platformName, String platformVersion, String apkPath, String appPackage, String appActivity) throws Exception {
log.i (TAG, "BeforeClass");
driver = setRemoteDriver (driverName, url, deviceName, platformName, platformVersion, apkPath, appPackage, appActivity);
actions = ElementActions.getInstance ().init (driver);
assertActions = actions.getAssertActions ();
Screenshot.getInstance ().init (driver);
prepare ();
}
Copy the code
Also AfterClass, driver exits.
Normally we write test cases like this:
A TestClass contains multiple testMethods. If each TestMethod is independent of the other, rerunning the APP would be time-consuming, so I’m going to make each TestClass independent of each other, and the testMethods in it depend on each other, in order of execution by X of TestNG ML to control. As follows:
<test name="XX test">
<classes>
<class name="XXTest">
<methods>
<include name="testAAA"/>
<include name="testBBB"/>
<include name="testCCC"/>
</methods>
</class>
</classes>
</test>
Copy the code
However, in some cases AfterMethod annotations can be used if TestMethod is also independent of each other.
@AfterMethod
public void afterMethod(a) {
driver.resetApp ();
}
Copy the code
Write a use case
Look for the element
Positioning way
Elements can be found in a number of ways (you can get the page ID,name, etc., via UIAutomatorViewer):
- id
- name
- className
- xpath
- uiautomator
Senior position
-
Find the login button with xpath:
by.xpath ("//button[@name='login']") Copy the code
Xpath Example Tutorial
- with
uiautomator
API scroll to find:
String rule = "new UiScrollable(new UiSelector().scrollable(true)).scrollIntoView(new UiSelector().textContains(\"" + locator.value + "\")"
WebElement cl = driver.findElementByAndroidUIAutomator(rule));
Copy the code
It’s just like if I write it in uiAutomator:
new UiScrollable(new UiSelector().scrollable(true)).scrollIntoView(new UiSelector().textContains(value));
Copy the code
uiautomator API
### Element managementCopy the code
In order to make test cases easy to reuse and simplify the time to write use cases, necessary encapsulation is necessary. The Page Object pattern is used to encapsulate elements on the Page. Then all tests need to do is simply control the page element.
- You can use yamL files to manage page elements:
You then wrap yamL parsing into Locator objects in BasePage. In this way, all PageObjects can use the following method to locate the element.
protected Locator getLocator(String locatorName) {
checkNotNull (locatorName);
Page page = getPage ();
List<Locator> locators = page.locators;
for(Locator locator : locators) {
if (locatorName.equals (locator.name)) {
returnlocator; }}return null;
}
publicLocator Enter the mobile phone number = getLocator ("Please enter your mobile phone number.");
publicLocator Enter verification code = getLocator ("Please enter the verification code.");
publicLocator Send verification code = getLocator ("Send verification code");
Copy the code
TODO: Using the tool to generate the corresponding Page class could have been done by using the IDE to indicate which controls were available, but writing the above code was a boring operation, so I used the Freemarker to automatically parse YAML to generate the corresponding Page class, as follows:
Map<String, Object> input = new HashMap<String, Object> ();
input.put ("pageName", page.pageName);
List<LocatorObject> genLocators = new ArrayList<> ();
List<Locator> locators = page.locators;
for(int i = 0; i < locators.size (); i++) {
System.out.println ("To generate a Locator." + locators.get (i).name);
genLocators.add (new LocatorObject (locators.get (i).name, locators.get (i).name));
}
input.put ("locators", genLocators);
Template template = cfg.getTemplate ("page-temp.ftl");
Writer fileWriter = new FileWriter (new File (path, page.pageName + ".java"));
try {
template.process (input, fileWriter);
} finally {
fileWriter.close ();
}
Copy the code
If you can’t use Freemarker, do a search.
Element interaction
Encapsulate interaction events in ElementActions. It’s very simple to use:
Actions. Text (loginPage. Please enter the phone number, phone); Actions.click (loginPage. Send verification code); Actions. text (loginPage. Please enter the verification code, PWD); Actions.click (loginPage. Login);Copy the code
Common interaction events: click, combos, scroll up, down, left, right, backward, enter text, and so on.
The element location above is really just wrapping a Locator, there is no actual lookup element, the lookup element is actually handled in ElementActions. Because applications often handle network requests, controls sometimes take a while to become visible, so you need to give them some time to find.
WebElement element;
try {
element = (new WebDriverWait (mDriver, locator.timeOutInSeconds)).until (
new ExpectedCondition<WebElement> () {
@Override
public WebElement apply(WebDriver driver) {
List<WebElement> elements = getElement (locator);
if(elements.size () ! =0) {
return elements.get (0);
}
return null; }}); }catch (NoSuchElementException | TimeoutException e) {
log.e (TAG, [%1$s], [By.%2$s: %3$s]", locator.name, locator.type, locator.value, locator.timeOutInSeconds);
throw e;
}
Copy the code
assertions
Also for ease of use are common assertion encapsulation, such as Toast validation.
public void validatesToast(final String msg) {
checkNotNull (msg);
final WebDriverWait wait = new WebDriverWait (mDriver, 10);
assertNotNull (wait.until (ExpectedConditions
.presenceOfElementLocated (By.xpath (String.format ("//*[@text=\'%s\']", msg)))));
}
Copy the code
The test report
The test report can intuitively display the success rate of the test, screenshots and other information. Here, extentReports is selected as the framework for test reports.
End result:
Hit the pit
findElementByName
Is invalid.
Searching by name was deprecated over a year ago and removed from 1.5. In general, searching by accessibility id is better for a variety of reasons.
The above findElementByName method was removed from Appium 1.5, but the API can’t find it and it’s not out of date. The following code was later used to solve the problem of finding elements by name.
String query = "new UiSelector().textContains" + "(\" " + locator.value + "\")";
webElements = mDriver.findElementsByAndroidUIAutomator (query);
Copy the code
- It is said that
Appium 1.6.3
We’re ready to look up Toast information. Then fart pidianpidian ran to try the example on the Internet found that it did not work. I thought it was the Client version. It took a long time to find the following code needs to be added:
capabilities.setCapability (MobileCapabilityType.AUTOMATION_NAME, AutomationName.ANDROID_UIAUTOMATOR2);
Copy the code
I feel the current Appium documentation is incomplete and a bit messy
- I accidentally found an error when autonamap played Toast during test. Then directly compile and install without saving. Guess if Appium changed something during installation. Look at the Service log and re-sign it at installation time…
App not signed with debug cert.
2017-02-13 18:17:19:848 - info: [debug] [ADB] Resigning apk.
2017-02-13 18:17:23:938 - info: [debug] [ADB] Zip-aligning 'app-debug.apk'2017-02-13 18:17:23:958 - info: [ADB] Checking whether zipalign is present 2017-02-13 18:17:23:964 - info: (ADB) Using the zipalign from/Users/mio4kon/Library/Android/SDK/build - the tools / 25.0.2 / zipalign 18:17:23 2017-02-13:968 - the info: [debug] [ADB] Zip-aligning apk. 2017-02-13 18:17:24:104 - info: [AndroidDriver] Remote apk path is /data/local/tmp/463eb03788048b4a1dacfe28545ee76e.apk
Copy the code
Solutions:
capabilities.setCapability (AndroidMobileCapabilityType.NO_SIGN, true);