Appium cross platform


Useful if simplistic info in below link however still using page model…

Cross platform testing example for Android and iOS using Appium

Snippet:

Another solution is to use the annotations @AndroidFindBy and @iOSFindBy. With this you can have the same element in the page object class with both annotations and thereby different locators for the different platforms. Example:

If the app you are testing is performing the exact same way in Android and iOS then the solution above would be enough for you to be able to write a generic test that works on both platforms. But unfortunately that is not always the case.

As described in issue 2 above there are often small deviations between the platforms for performing the same task. Because of this you will need different steps for the different platforms for executing the same scenario. One way to do this is to implement an interface for each page under test. The interface define the methods used in the tests to execute the specific scenario. You can then have different page object files for the Android and the iOS pages that implements the interface methods in their own way. With this you have the solution for both issue 1 (since you have separate page object files for Android and iOS) and issue 2 (since the separate page object files can have different implementation of the same interface method used in the test).

To demonstrate this a bit more clearly I have created an example project with two simple applications for Android and iOS and a corresponding testcase using the “design pattern” described above. You can find the project here:

https://github.com/ThomasHansson/Appium-cross-platform-example

xx

Lahiru
February 1, 2017 at 07:22 / Reply
Really a nice article found after a million searches .Tx a Lot Thomas , Between is there a way to implement web view as well since this uses APK ?

Thomas Hansson
February 1, 2017 at 11:15 / Reply
Do you mean if there is a way to implement test that interact with webview elements inside your application? In that case, yes there is.
When you are using appium, you normally operate in the native context. But if your app contains an embedded webview, there will also be a webview context avaliable. You can get all the avaliable contexts by calling driver.getContextHandles() which will then return something like [“NATIVE_APP”, “WEBVIEW_1”]. If you want to automate parts of the UI that are inside the webview, you need to tell appium to switch context to the webview with driver.context(“WEBVIEW_1”). After this all commands sent by your test will apply to elements inside the webview, instead of elements which are part of the native UI. If you want to control part of the Native UI again, you then need to return to the native context with driver.context(“NATIVE_APP”).


xx

 

Automated testing -> Useful short version

  • http://selendroid.io/
  • http://ios-driver.github.io/ios-driver/#real

  • Hamcrest matchers
    • replace asserts
    • high degree of complexity
    • easy to understand
    • plain (useful) logging?
    • Examples:
      • allOf – matches if all matchers match (short circuits)
      • anyOf – matches if any matchers match (short circuits)
      • not – matches if the wrapped matcher doesn’t match and vice
      • equalTo – test object equality using the equals method
      • is – decorator for equalTo to improve readability
      • hasToString – test Object.toString
      • instanceOf, isCompatibleType – test type
      • notNullValue, nullValue – test for null
      • sameInstance – test object identity
      • hasEntry, hasKey, hasValue – test a map contains an entry, key or value
      • hasItem, hasItems – test a collection contains elements
      • hasItemInArray – test an array contains an element
      • closeTo – test floating point values are close to a given value
      • greaterThan, greaterThanOrEqualTo, lessThan, lessThanOrEqualTo
      • equalToIgnoringCase – test string equality ignoring case
      • equalToIgnoringWhiteSpace – test string equality ignoring differences in runs of whitespace
      • containsString, endsWith, startsWith – test string matching
    • http://crunchify.com/how-to-use-hamcrest-assertthat-matchers-to-create-junit-testcases-in-java-complete-tutorial/

xxx

Serenity Test Automation

Focus on screenplay pattern:

(

Reminder on ‘SOLID’:

  • http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod
  • https://scotch.io/bar-talk/s-o-l-i-d-the-first-five-principles-of-object-oriented-design

The first five principles are principles of class design. They are:

SRP The Single Responsibility Principle A class should have one, and only one, reason to change.
OCP The Open Closed Principle You should be able to extend a classes behavior, without modifying it.
LSP The Liskov Substitution Principle Derived classes must be substitutable for their base classes.
ISP The Interface Segregation Principle Make fine grained interfaces that are client specific.
DIP The Dependency Inversion Principle Depend on abstractions, not on concretions.

)

http://testerstories.com/category/automation/serenity/

Thoughts on Page Model

Page model alternatives:

“Journey” or “Screenplay’ pattern

https://www.infoq.com/articles/Beyond-Page-Objects-Test-Automation-Serenity-Screenplay

A weak Journey example: http://javing.blogspot.co.nz/2015/12/selenium-automation-design-pattern.html

SOLID:

https://ideas.riverglide.com/page-objects-refactored-12ec3541990#.ac0o42osj

 

 

 

http://michaelpavlakcoding.blogspot.co.nz/2016/06/decomposing-page-objects.html

 

https://confengine.com/selenium-conf-2014/proposal/348/design-patterns-beyond-the-page-object-an-investigation-into-the-design-patterns-used-while-building-page-objects

 

State modeling:

http://queue.acm.org/detail.cfm?id=2793039

 

 

 

 

Snapshot -> mestachs.wordpress.com -> Selenium Best Practices

Useful per:

mestachs.wordpress.com/2012/08/13/selenium-best-practices/

Still mostly applies. Snapshotting here for my own benefit as that site is stale.


Selenium Best Practices

It’s a summary (and few extras) of test_design_considerations

Use PageObjects pattern
Be fluent with 
  - return this, varargs, generics, 
  - reuse your model and jodatime
Be robust and portable 
  - Prefered selector order : id > name > css > xpath 
  - Avoid Thread.sleep prefer Wait or FluentWait
  - Use relative URLs
  - Don’t rely on specific Driver implementation
  - Create your dataset
Know your new tool
  - Keep up to date (versions and usage pattern)
  - Troubleshooting 
      - jre 1.6
      - IE (zoom, Protected mode setting )
      - Firefox/firebug startpage
  - How to deal with UI components like... fileupload, datepicker, ajaxtables,...
  - Detect when selenium isn't the good tool for the job
  - Don't be afraid to hack around selenium

Use PageObjects pattern

Page Object is a Design Pattern which has become popular in test automation for enhancing test maintenance and reducing code duplication. A page object is an object-oriented class that serves as an interface to a page of your Application Under Test. The tests then use the methods of this page object class whenever they need to interact with that page of the UI. The benefit is that if the UI changes for the page, the tests themselves don’t need to change, only the code within the page object needs to change. Subsequently all changes to support that new UI are located in one place.

The Page Object Design Pattern provides the following advantages :

  • There is a clean separation between test code and page specific code such as locators (or their use if you’re using a UI map) and layout.
  • There is  a single repository for the services or operations offered by the page rather than having these services scattered through out the tests.

For example you have a LoginPage that you can reuse in all your integration test.
if the logon has now a new option or the layout changed a bit… adapt the LoginPage… that’s it !

More on page object : page-objects-in-selenium-2-, PageObjectFactory, simple example based on google search page

Be fluent !

With return this

It makes often your tests more readable if your “void” methods return “this”.

1
2
3
4
5
6
public class CampaignCreatePage extends AbstractPage {
    ...
 public CampaignCreatePage withCampaignName(String campaignName) {
     name.sendKeys(campaignName);
     return this;
 }

So you can chain the calls like :

1
2
3
4
5
6
7
8
9
10
11
@Test
public void shouldntSaveWithoutAnyErrors() throws Exception {
    CampaignCreatePage createPage = openCampaignCreatePage();
    createPage.hasNumberOfErrors(4);
    createPage.withCampaignName("SELENIUM").save();
    createPage.hasNumberOfErrors(3);
    createPage.withAgent("firefox").save();
    createPage.hasNumberOfErrors(2);
    createPage.withAssignee("tom").save();
    createPage.hasNumberOfErrors(1);
}

With varargs

You can also use varargs to make the api cleaner

1
2
3
4
public CampaignCreatePage withCampaignTargetsIds(long... targets) {
    targetsIds.sendKeys(StringUtils.join(target,","));
    return this;
    }

In the test this give us something like

1
2
3
4
5
6
@Test
public void shouldntSaveTargetUnknownIds() throws Exception {
    CampaignCreatePage createPage = openCampaignCreatePage();
    createPage.withCampaignName("SELENIUM").withAgent("firefox").withAssignee("tom").withProduct(0).withCampaignTargetsIds(1, 2, 3, 4);
    createPage.save().hasNumberOfErrors(1);
}

With generics

If you have a component that don’t have access to your pageObject. For example, a generic menu where plugins can contribute links to their own pages.
you can use this trick :

1
2
3
4
public  T clickOnMenu(String path, Class pageClassToProxy) {
    clickOnMenu(path);
    return PageFactory.initElements(getDriver(), pageClassToProxy);
}

 

1
2
3
4
5
6
7
8
   @Test
   public void testBrodocWithoutMemberOrProspectIdentified() {
       processLogon();
       MemberIdentificationPage page = openMemberIdentificationPage();
       page = page.getMenuElement().clickOnMenu(LuceneSearchPage.URL, LuceneSearchPage.class);
       page.hasInfoMessage();
       page.withNameFilter("mestachs").search().getResults().hasResults()
}

By reusing your model and joda

For example the CampaignSearchPage can reuse the SearchCampaignParameter

1
2
3
4
5
6
7
8
9
10
11
12
...
     public CampaignSearchPage searchFor(SearchCampaignParameter parameter) {
        clearForm();
        selectRadio("campaignTargetType", parameter.getPersonType());
        sendString(campaignName, parameter.getCampaignName());
        sendDate(creationDateFrom, parameter.getCreationDateFrom());
        sendDate(creationDateTo, parameter.getCreationDateTo());
        sendDate(campaignDateFrom, parameter.getCampaignDateFrom());
        sendDate(campaignDateTo, parameter.getCampaignDateTo());
        findCampaigns.click();
    }
...

Be robust and portable

Preferred selector order : id > name > css > xpath

To locate an element we can use

  • the element’s ID
  • the element’s name attribute
  • an XPath statement
  • by a links text
  • document object model (DOM)

In practice, you will discover

  • id and name are often the easiest and sure way.
  • xpath are often brittle. for example you have 3 tables displayed but sometimes there are no data and the table isn’t rendered, your xpath locating the second table will not always return the intented one.
  • css are the way to go in conjunction of id and name !
  • locating links in an internationalized application is sometimes hard… try to use partial href.

various selectors articles : why-jquery-in-selenium-css-locators-is-the-way-to-go, css selector demystified, jquery selectors

Avoid Thread.sleep prefer Wait or FluentWait

Instead of sleep

1
2
3
4
public void clickOnContactType() {
    getDriver().findElement(By.id("contacttypelink")).click();
    sleep(500);
}

Prefer this fluentWait

1
2
3
4
public void clickOnContactType() {
    getDriver().findElement(By.id("contacttypelink")).click();
    SeleniumUtil.fluentWait(By.name("handle"), getDriver());
}

It’s more robust, deterministic, in case of element not found… the exception will be clearer.

Another alternative is

1
2
3
4
(new WebDriverWait(driver, 30)).until(new ExpectedCondition() {
                public Boolean apply(WebDriver d) {
                    return d.getTitle().toLowerCase().startsWith("java developer");
                }

More on this http://www.thoughtworks-studios.com/twist-agile-test-automation/2.3/help/how_do_i_handle_ajax_in_selenium2.html

Use relative URLs

Avoid hardcoding hosts

1
2
3
WikiMainPage page = PageFactory.initElements(getDriver(), WikiMainPage.class);
page.search("sdfsdf");

Prefer configuration and pass relative urls like

1
openPartialUrl("/");

That use an abstract method

1
2
3
4
5
6
7
8
9
10
11
12
13
protected void openPartialUrl(String partialurl) {
    getDriver().get(getUrlPrefix() + partialurl + "?siteLanguage=" + this.settings.getLanguage());
}
private static String getPrefix() {
    return StringUtils.replace(System.getProperty("selenium.website.url", HTTP_PREFIX), "@localhost@", getCanonicalHostName());
}
private static String getCanonicalHostName() {
    try {
        return java.net.InetAddress.getLocalHost().getCanonicalHostName();
    } catch (Exception e) {
        return "127.0.0.1";
    }
}

It is easy then to launch these tests against another server (integration,acceptance,… or jetty in integration tests)

Don’t rely on specific Driver implementation

Don’t assume that driver will be an instance of FireFoxDriver or InternetExplorerDriver. For example when running the integration tests on your continuous build (linux) you will receive a RemoteDriver.

It’s quite easy to create a small framework around selenium using :

  • LabelledParameterized : to be able to run with different browsers IE,FF,… or with different user language fr/nl/en
  • ScreenShotWatchMan : that takes screenshot on exception and logs the html sources. (see ./target/screenshot/*.png)

Create your dataset

Avoid something like assuming that the data is always there.

1
2
3
4
CampaignConsultPage consult = openCampaignConsultPage(2132103215L);
CampaignEditPage edit = consult.goToEditPage();
consult = edit.save();
consult.hasInfoMessage();

Create a new campaign and check consult

1
2
3
4
5
6
7
8
9
CampaignCreatePage createPage = openCampaignCreatePage();
createPage.withCampaignName("SELENIUM" + new DateTime()).withAgent("tom").withAssignee("tom").withProduct(0);
createPage.save().hasNumberOfErrors(0);
createPage.hasInfoMessage();
Long campaignId = createPage.getCampaignId();
CampaignConsultPage consult = openCampaignConsultPage(campaignId);
CampaignEditPage edit = consult.goToEditPage();
consult = edit.save();
consult.hasInfoMessage();

Know your new tool

Keep up to date

Keep your binaries and knowledge up to date !
This official blog contains announcements and also some a lot of links to possible application of selenium
StackOverflow forums are really good to learn from other mistakes. The official Sauce Labs Blog is also really interesting for their real life experiences.

Use jre 1.6

If while starting your integration tests you encounter :

java.lang.NoSuchFieldError: HOURS
at org.openqa.selenium.remote.internal.HttpClientFactory.(HttpClientFactory.java:44)

or

java.lang.NoSuchFieldError: java/util/concurrent/TimeUnit.HOURS

Run your test with a jre 1.6

How to close Firebug startpage

FirefoxProfile profile = new FirefoxProfile();
profile.setPreference("extensions.firebug.currentVersion", "1.8.2");
driver = new FirefoxDriver(profile);

note that you may need to adapt the version.

IE not working

check

  • The browser zoom level must be set to 100% so that the native mouse events can be set to the correct coordinates.
  • On IE 7 or higher on Windows Vista or Windows 7, you must set the Protected Mode settings for each zone to be the same value. The value can be on or off, as long as it is the same for every zone. To set the Protected Mode settings, choose “Internet Options…” from the Tools menu, and click on the Security tab. For each zone, there will be a check box at the bottom of the tab labeled “Enable Protected Mode”.

Possible workaround for Protected Mode

1
2
3
4
DesiredCapabilities ieCapabilities = DesiredCapabilities.internetExplorer();
ieCapabilities.setCapability(InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS, true);
ieCapabilities.setCapability("ensureCleanSession", true);
driver = new InternetExplorerDriver(ieCapabilities);</pre>

How to deal with ui elements like

File upload

Selenium Tips: Uploading Files in Remote WebDriver

Single and multi select

One option

1
2
3
4
5
6
7
public class AbstractPage {
...
   protected void selectRadio(String name, String code) {
    WebElement select = this.webDriver.findElement(By.xpath("//input[@value='" + code+ "'and @name='" + name + "' ]"));
    select.click();
   }
...

Maybe a better option is using org.openqa.selenium.support.ui.Select

1
2
3
4
5
6
7
8
Select singleSelection = new Select(getDriver().findElement(By.id("single-selection")));
singleSelection.selectByVisibleText("July");
Select mulitpleSelection = new Select(getDriver().findElement(By.xpath("//select[@multiple='multiple' and @size=12]")));
mulitpleSelection.deselectAll();
// Select February, August and November using different functions.
mulitpleSelection.selectByIndex(1); // February
mulitpleSelection.selectByValue("Aug"); // Select ...
mulitpleSelection.selectByVisibleText("November"); // Select November

DatePicker

1
2
3
4
public class CampaignSearchPage extends AbstractPage {
  ...
    sendDate(creationDateFrom, parameter.getCreationDateFrom());
  ...</pre>

sendDate is defined in AbstractPage

1
2
3
4
5
protected void sendDate(WebElement e, DateMidnight dm) {
 if (dm != null) {
     sendString(e, dm.toString("dd/MM/YYYY"));
 }
}

AjaxTableElement

If you use table elemenst with paging, sorting,… you can perhaps create a component AjaxTableElement
add in pageObject :

1
2
3
4
5
6
public class CampaignSearchPage extends AbstractPage {
          ....
         public AjaxTableElement getTableResult() {
        return new AjaxTableElement(getDriver(),"results");
    }
}

Then you can use the AjaxTableElement

1
2
3
page.searchFor(param);
page.getTableResult().hasResult();
page.getTableResult().clickOnResult(1);

Detect when selenium isn’t the good tool for the job

Selenium can’t deal with OS level dialogs so first avoid them as much as possible…
For eg don’t try to download a pdf through selenium and open acrobat reader.
You may be tempted to use tool like : autoit or sikuli (his java api) but you will loose portability and may be they will only offer you a false sense of quality.

Don’t be afraid to hack around selenium

A lot of people are using it but also trying new stuff like :
– collecting performance statistics.
– creating a full acceptance framework
– implementing a new webdriver

Thoughts on Cucumber for Acceptance testing 20170201-20170202

Just a tumbledown of thoughts

Latest first


20170202

Fitnesse / RobotFrameWork, alternatives?

Criteria:

  • Selenium integration
  • Rest API testing facilitated would be nice
  • Active project (not stale)
  • Java & C# possible
  • Self hosted

Footnote: http://serenity-bdd.info/#/ arrrg uses BDD scripts (Cucumber, JBehave etc + variants)


20170202

Cucumber or something lighter (from a user perspective)?

Consolidating my thinking and with pertinence to a banking application..

  • A large proportion our tests are ultimately data centric:
    • either transactional (input = actions/data, output = validate)
      • financial
      • state change e.g. card stop/start
      • applications
    • state validations (input = state, output = validate)
      • e.g. Cannot transfer to account in state xyz
  • A highly desirable outcome would be for non technical users/test analysts to be able to run their own tests

So assuming we implement a tool/framework of some sort over the top of Selenium for users to create tests:

Cucumber / Gherkin:

  • Too verbose.
    • If a test is only possible by a logged in user, then the fact the user is logged in it doesn’t need to be stated in the feature
    • If we need to test a user can / can’t do things based on logged in, then make that a separate feature
  • Verbosity leads to too many step definitions
    • Users need index of known step definitions, so how documented?
    • Needless level of detail

Which leads me to leaner user centric tools like Fitness (over Fit/Slim) or the Robot Framework.

EOM


20170201

Cucumber?

JeffL, Java dev at MSD was enthusiastic about using Cucumber for ‘testing’

The implication was not for unit testing but for end to end / acceptance testing.

But everything I read (and mostly by the guys who made up cucumber) say DON’T use for other than TDD/BDD requirements gathering / dev process.

I.e. specifically not for end to end etc.


So, that forces me to think about what I want to achieve in no particular order, and why do people gravitate to Cucumber..


I want to:

  • Perform automated testing of an application across 3 platforms:
    • Browsers
    • iOS mobiles
    • Android mobiles
    • Note: Currently browser is the functional superset with mobile apps > 80% functionality implemented

We can safely assume we will use Selenium toolset (+ Java) to drive through to the various platforms.

Given that our functionality is at least 80% common across the 3 platforms, I’ve already had a think about selenium page models that allow our tests to be as platform agnostic as possible.

JAVA per page modelling:

  • Base class e.g. PayAndTransfer
    • abstract properties / methods to set template
    • as many methods as possible are complete
  • Extension classes per platform e.g. PayAndTransferBrowser, PayAndTransferAndroid
    • complete abstracts etc
    • methods overridden only where necessary
  • Factory: PayAndTransferFactory accepting platform enum {Browser, Android, iOS}
    • switch(platform).. return new PayAndTransferAndroid etc

As an aside, I’ve never quite agreed with the view that validations should not be performed inside our page models.  I’d prefer our page models be self validating where practical / possible.  If in my test I say do X, I expect the page model to validate that x actually occurred.  I acknowledge there is a tradeoff with regards to execution time so have yet to completely form my view on this.


So now the big one…how do we construct our tests.

Simplest:

  • TestNG (JUnit) in Java IDE

But ideally, anyone should be able to write our tests, give an adequate provided framework.. so I imagine this is where cucumber gets some traction as a testing tool.

https://www.ibm.com/developerworks/library/a-automating-ria/

But where I struggle with Cucumber is the Feature file interpreters.  I envisage a lot of coding required here if I follow the more simplistic examples provided.

Other solutions:

EOM