Starter pack on Penetration/Security Testing for newbies

As an experienced Tester, recently I have been endeavouring to grow my Penetration & Security Testing skills.

As with any new skill-set the journey can get overwhelming very quickly , because of the vast number of concepts, new terminologies, lack of dedicated mentorship and research sources .

Based on my learning and explorations over the past few months in the Pen Testing & Cyber Security realm, I am putting together a table a learning goals and resources that i hope will help Testers start out on their journey in Pen Testing .

Not by any stretch this is a replacement for real world project experience or structured certification training like OSCP , but is rather aimed as full-time Test Professionals, who on the side are interested in learning about security challenges & Pen Testing for Web,Network and Mobile apps.

Learning goal/research topicResources
What are some of the most common security weaknesses out there?OWASP Top 10

https://owasp.org/www-project-top-ten/
How can you inspect HTTP requests/responses, view source code, manipulate cookies etc using Chrome Dev tools ? https://developers.google.com/web/tools/chrome-devtools
Why is Kali Linux so popular for Pen Testing practitioners ? How can you install Kali Linux using Virtual Box ? https://www.kali.org/docs/introduction/what-is-kali-linux/
Set up your own instance of Kali Linux and if you are new to Linux , handy to go through this –>
https://tryhackme.com/module/linux-fundamentals
Where can you find apps that are deliberately vulnerable ?
The common Pen Testing approach for all tool sets below is –
You have a machine + OS ( like Kali Linux) to be your “attacker” machine, i.e. from where to run the tools to find weaknesses in the “target” machine or a machine hosting the vulnerable app.
https://github.com/kaiiyer/awesome-vulnerable

https://pentester.land/cheatsheets/2018/10/12/list-of-Intentionally-vulnerable-android-apps.html
How do you scan a web app for vulnerabilities ? Start with ZAP proxy – https://www.zaproxy.org/getting-started/
Application of ZAP proxy to detect common weaknesses in Web apps
https://www.zaproxy.org/docs/guides/zapping-the-top-10/
then explore Nessus –
https://resources.infosecinstitute.com/topic/a-brief-introduction-to-the-nessus-vulnerability-scanner/
What does everyone rave about Burpsuite ?
What capabilities does it provide to perform scanning and penetration attacks ?
Starting with Burpsuite ->
https://dev.to/leading-edje/getting-started-with-burp-suite-31hd#articles-list

OWASP Top 10 detection using Burpsuite –>
this is quite intense, but well worth the learning
https://portswigger.net/support/using-burp-to-test-for-the-owasp-top-ten
What is Network reconnaissance ?
Which is a beginner’s tool to scan your network for gathering information ?
Watch this series of excellent tutorials on Nmap from YouTuber – Hackerspoilt
https://www.youtube.com/watch?v=5MTZdN9TEO4
Are there any tools solely focussing on trying to exploit sql databases ?
Yes, SQLMap is one that is preinstall on Kali Linux , that you can use to try & penetrate a vulnerable website

https://www.kalitutorials.net/2014/03/hacking-website-with-sqlmap-in-kali.html
How to get started with Android Pen testing ? Understand Android architecture and how Android apps are built ?

https://medium.com/mobile-penetration-testing/00-prepare-for-penetration-testing-cea4c3de1f05

Use one of the traffic sniffing tools ( e.g Burp Suite proxy) to intercept traffic from an Android app

https://medium.com/androgoat/intercept-http-traffic-from-android-app-androgoat-6e3d4d14d352

This is intense again , but going through these tutorials really helped me get a understanding common Android vulnerabilities and how to detect them ?

https://medium.com/mobile-penetration-testing/android-penetration-testing-courses-4effa36ac5ed


How do you reverse engineer apk files and study application code for static verification ?APK tool and JADX GUI are two reverse engineering tools that i used

https://ibotpeaches.github.io/Apktool/

https://ourcodeworld.com/articles/read/387/how-to-decompile-an-apk-or-dex-file-using-jadx-in-windows

Are there any “Security as a Service” type of scanners for apps ? I explored and played with 3 –

MobSF https://github.com/MobSF/Mobile-Security-Framework-MobSF
Python based and you have to install it locally

Ostor Lab – A cloud based service where you can upload your app and run vulnerability scans on it
https://www.ostorlab.co/

Immuni Web – Another cloud based service
https://www.immuniweb.com/

Other tools that I have come across but have not used yet

Intruder.io
Infection Monkey – Simulates breaches & attacks on your Network
Going deeper into Mobile Application Security

This book by the OWASP Team is excellent and has great hands on material

https://owasp.org/www-project-mobile-security-testing-guide/
Self Training and hacking practice platforms I have primarily used TryHackMe and their paid service , found it will worth the 10 $ per month that they charge
https://tryhackme.com/

There is another one, I have have come across but not used yet – https://www.hackthebox.eu/

Quick starter – Web automation using Playwright

Playwright is a (relatively) new kid on the block, joining several others kids already on the block, that are the JavaScript based automation frameworks.

I learnt about it from a mention on Twitter and thought to give it a whirl.

This post is purely meant to be a sketchy guide on getting started with it ( with less focus on it’s overall architecture & philosophy, however, importantly, for that start with the docs here )

Toolkit for this post –

  • JavaScript as the programming language. Playwright has a Python based library too !
  • VS Code as IDE
  • Chrome as the browser
  • OS = macOS

Installation and set up –

  • Install playwright framework using npm on your terminal –> npm i -D playwright
  • Check Node.js has been installed successfully and is in your path –> node command on your terminal should return a prompt like below. Ctrl+C twice to exit.
  • Lets create a simple .js file that we will use to write our test and run it using Node.js
  • Ensure Google Chrome’s canary build is installed at default location –> /Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary

Build code for your test –

  • Test is — Visit a contemporary e-commerce web site, browse for some items, then login user a username & password combination , but the combo is incorrect, assert on the error message displayed
  • Code pattern followed is async & await
  • Declare that you will use chromium based browser for your testing –>
  • Declare that you will use inbuilt assertions in your test –>
  • Write a snippet to launch an instance of Chrome, headful, by providing a path to the chrome canary and navigate to the webapp under test.
  • Selecting elements is a breeze and no need to write waits ! Can select elements based on text easily
  • Find our dynamic error message on a failed login ,get it’s content and perform a rudimentary assertion on it
  • Run ! –> node <file_name>.js

Putting it all together …

//@ts-check
const { chromium } = require('playwright');
const { assert } = require('console');
(async () => {
// launch a chrome instance from the path provided , not in headless mode and with the slowMo value set for slower playback
const browser = await chromium.launch({headless: false,executablePath: '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',slowMo: 500});
// playwright has the concept of giving context to your browser object –> https://playwright.dev/#version=v1.4.0&path=docs%2Fcore-concepts.md&q=browser-contexts
const context = await browser.newContext();
// from a context spawn your page object, the primary medium to perform broswer automation
const page = await context.newPage();
// lets head over to the home page of out website
await page.goto('https://theiconic.com.au&#39;)
// oh,dealing with a pesky pop up is easy peasey,did not have to write waits etc,just had to enter text of the button to be used as a selector !
//playwright's inbuilt auto-wait capability — https://playwright.dev/#version=v1.4.0&path=docs%2Factionability.md&q=
await page.click('text=No Thanks')
// perform navigation to another page of the app using text as a selector.
// More on selectors here – https://playwright.dev/#version=v1.4.0&path=docs%2Fselectors.md&q=
await page.click('text=#stayhome')
// head over to the login page
await page.goto('https://www.theiconic.com.au/customer/account/login/&#39;)
// select an element by id and click on it
await page.click('id=LoginForm_email')
// or just directly filling it with text
await page.fill('id=LoginForm_email','zookeeper@nationalzoo.com')
// another element found easily by id and text entered
await page.fill('id=LoginForm_password','tununutunu')
// find by id and click the login button
await page.click('id=LoginForm_submit')
// lets find the text contents of the selector below , just have pass the selector to the page.textContent method
const login_error = await page.textContent('#form-account-login > div:nth-child(2) > div:nth-child(2) > div');
console.log(login_error)
// perform a simple assertion
assert(login_error=='The email address or password you entered is incorrect. Please try again.1')
// we are done 🙂
await browser.close();
})();
view raw playwright.js hosted with ❤ by GitHub

Initial Playwright experience

  • Found writing the test very intuitive
  • Loved not having to write explicit waits
  • Stoked about the straightforwardness of dealing with selectors

Will definitely explore Playwright more !

Lessons learnt from a POC to automate Salesforce Lightning UI

My recent client work has been on testing a migration(data & business processes) to Salesforce CRM platform.

As part of Test execution, I took the initiative to build a POC to exercise automation of Salesforce both by interacting with the Lightning UI and the APEX Salesforce API interface.

This post is to share the hurdles I faced and lessons I learnt in building the POC for UI automation.

1. Choice of tools – Cypress.io & Selenium WebDriver

I exercised two tools sets that I am experienced with for UI automation – Cypress.io and Selenium Webdriver API (using Python) .

I could not go far wth Cypress, as it has limited support for iframes ( by design) , covered in this open issue thread.

Basically, as soon as I automated the login process, hit an error where Cypress terminated with an error “Whoops there is no test to run”

https://github.com/cypress-io/cypress/issues/2367

I tried some of the workarounds mentioned in the thread that worked for some folks, but not success.

So, once I exhausted my time box for Cypress I moved onto Selenium Webdriver.

2. Bypassing Email verification after login

The first hurdle I hit was the email 2FA that was set on the Salesforce sandbox environment that I was testing.

If I would have been automating at the API layer, there are various secure ways (e.g. API tokens) to authenticate but with email verification from the UI,Salesforce bases it on IP addresses. So, to work around that I to create another test user & profile that had either my IP whitelisted or basically no-IP filtered out.

Instructions from here were helpful –> https://github.com/neozenith/testcafe-sfdc-poc

3. Explicit waits & dealing with dynamic UI ids

Goes without saying that we have to explicitly wait for elements to load, to create robust tests, I had to write heaps of explicit waits to the interact with the Lightning UI (as it is a very “busy” UI)

Another interesting quirk I found , was ,even though some elements that I wanted to interact with has unique ids that I could as a selector, but as I found later through flakiness in my tests, that those ids especially for modal dialogs were being generated dynamically, most likely per login.

e.g.

this piece of code although using id, was flaky because webdriver could not find the same id across sessions. The value “11:1355;a” would change to “11:3435;a” on subsequent test runs for the same modal dialog box.

So, instead I went with a different approach to not use id for the dynamic modal dialogs, instead search by XPath in this case and awaiting for element to be clickable

That worked and finally I was able to complete a basic UI automation flow of logging in , interacting with dynamic elements, adding user input and asserting some test conditions 🙂

A genteel peek into GitLab CI/CD

My CI engine of choice & experience as a Tester has been Jenkins.

One of the strategic projects in the pipeline at my current client is to adopt GitLab as a solution for SCM,Continuous Testing and potentially Continuous Deployment .

And that project involves porting a Test Framework ( that I was fortunate to lead create) based on Python/Behave/PyTest running on  Jenkins to GitLab

Even though the project is in the pipeline, I thought to flirt with the idea of doing a wee POC to explore GitLab’s CI/CD .

Objective –

As a novice GitLab user, I would like to set up a trivial build pipeline, so that I can run a piece of Python code on every commit 

Approach –

1. Understand how GitLab’s CI/CD architecture works

2. Sign up for GitLab & set up a project

3. Set up a vehicle to execute your code (aka a “runner”)

4. Write instructions to build your pipeline (aka the “.gitlab-ci.yml” file)

5. See the magic happening i.e. output of your Python code being rendered

Step 1 – (A simplistic view of) GitLab’s CI/CD architecture 

To get a pipeline up and running, you need need components to be talking to each other.

  • A GitLab instance to act as a code repo and host of your project
  • A YAML file that has pipeline details like platform to run on, build steps, shell commands etc , and acts as the orchestrator
  • A local or remote machine to check out code & run the instructions in the YAML file

GitLab

Step 2 – Sign up and create a project in GitLab 

SignIn/Register on GitLab here -> https://gitlab.com/users/sign_in

and create a blank project –

Screen Shot 2020-05-02 at 7.35.19 PM.png

 

Step 3 – Configure a runner  

I decided to use my machine ( MAC OS X) as a runner and these are the steps that I took

  1. Install –  https://docs.gitlab.com/runner/install/
  2. Register your runner – This is a critical step, that will make the GitLab instance know about your local runner. https://docs.gitlab.com/runner/register/ . I chose my runner to be a shell, just for the purposes of keeping this a simple exercise. Important to remember the tag for your runner here, as we will use that to call the runner from the YAML file
  3. Enable this runner in your project settings and disable shared runners. Click “expand” on runners in the settings for your project as below and on the next page click “disable shared runners”Screen Shot 2020-05-03 at 11.54.09 AM.png
  4. If everything is set up correctly, you should see your runner being detected in the settings page as follows . Note the tag “smoke” that I used in step 2 above Screen Shot 2020-05-03 at 11.57.22 AM.png

 

Step 4 – Set up build script 

In this step we will add the gitlab-ci.yml file to the project and make it

a) call & run a python file,

b) on the runner that we configured above

  1. Goto your project homepage & click on “set up CI & CD” Screen Shot 2020-05-03 at 1.13.50 PM.png
  2. On the next page you will be presented a web IDE to create the YAML file, I choose one of the many useful templates available fo my  script Screen Shot 2020-05-03 at 1.18.03 PM.png
  3. My pipeline script is very simple, this should be self explainable , note the bit with the “tags” is what calls the runner ! Screen Shot 2020-05-03 at 1.28.04 PM.png
  4. Lastly, add to the project the python file being called in the pipeline scriptScreen Shot 2020-05-03 at 4.41.01 PM.pngScreen Shot 2020-05-03 at 4.37.44 PM.png

 

Step 5 – Trigger the pipeline

The pipeline YAML script gets trigger automatically on every commit or you can goto Pipelines on the left navigation menu and click the “run pipeline” button

Screen Shot 2020-05-03 at 4.44.42 PM.png

Here is what the output looks like running on a bash shell , as you can see the echo output, output for pwd command and the Python file !

Screen Shot 2020-05-03 at 4.48.09 PM

so, there you go we have successfully set up a basic pipeline in GitLab that run a simple python script on a bash shell. I have liked what I have seen of GitLab so far and will explore more

 

 

 

 

Pen Testing reconnaissance 101 : Using NMap,Tor and ProxyChains

Learning objective : How can you perform reconnaissance on a remote target to check which ports are unsecured for possible exposure to network attacks?

Step 1: Create or choose an off the shelf Network Port scanner.

Based on my research and talking to more experienced peers in this space, I choose Nmap ( https://nmap.org/) , a free & open source network security auditing tools. It is very popular among researchers and professionals alike .

I’m using a MAC and one can either choose to install using the DMG file or using Homebrew

$ brew install nmap

Nmap has it’s downsides in terms of being “noisy” and easily detectable in terms of the amount of traffic it creates while performing it’s operations.

That brought me to step 2

Step 2: Find an anonymous way to run your Network Port scanner and perform reconnaissance in a securer fashion

Find a secure “overlay” to pass you traffic through so that you can anonymously use Nmap and not be exposed to exploitation yourself. Tor network and it’s Tor browser is what I chose

Here is an excerpt from an excellent intro guide on Tor ,

You may know Tor as the hometown of online illegal activities, a place where you can buy any drug you want, a place for all things illegal.  Tor is much larger than what the media makes it out to be. According to Kings College much of Tor is legal.

When you normally visit a website, your computer makes a direct TCP connection with the website’s server. Anyone monitoring your internet could read the TCP packet. They can find out what website you’re visiting and your IP address. As well as what port you’re connecting to.

If you’re using HTTPS, no one will know what the message said. But, sometimes all an adversary needs to know is who you’re connecting to.

Using Tor, your computer never communicates with the server directly. Tor creates a twisted path through 3 Tor nodes, and sends the data via that circuit.

The core principle of Tor is onion routing which is a technique for anonymous & secure communication over a public network. In onion routing messages are encapsulated in several layers of encryption.

Step 3: Stringing above tools together to execute a reconnaissance

The plan from here is to call Nmap commands from the terminal and redirect traffic through the Tor network ( that the Tor browser initiates when an instance is launched on the local machine – default for Tor is 127.0.0.1 9050)

Further research, led me to a useful tool called Proxychains. It is a unix based OS tool that marries really well with Tor(it is configured to redirect Tor traffic be default) or any other proxy or in fact chain proxies together to redirect traffic out from your local host.

Note – In terms of this part of the post I have not yet researched a windows equivalent for ProxyChains, so the end to end solution is incomplete in that regard.

So,

a) install ProxyChains using HomeBrew – $ proxychains4 brew install proxychains-ng

b) install and run Tor service from the command line –

$ brew install tor

$ brew services start tor

c) choose a target , that allows ethical pen testing . I chose Nmap’s offering called – scanme.nmap.org

d) Goto proxychains.conf file (usually found in /usr/local/etc folder) and if your installation was successful you see already see an entry saying –

[ProxyList]

# add proxy here ...

# meanwile

# defaults set to "tor"socks4

127.0.0.1 9050

e) You are all set now to run, nmap command through Tor ,on your terminal type ->

$ proxychains4 nmap -sT -PN -n -sV -p 21 scanme.nmap.org

The switches in the above command mean –

-sTfull TCP connection scan
-PNdo not perform host discovery
-nnever perform DNS resolution (to prevent DNS leaks)
-sVdetermine service version/info
-pports to scan

i.e. We are scanning port 21 on scaneme.nmap.org anonymously through nmap and see if it is open or closed?

f) The output will look something like ->

As you can see above , the request has been denied and state of the port is closed.

So, there you are , a simple basic Pen test to perform port scanning in a “safe” environment.

Further considerations with this approach and homework –

It is common for hosts to block Tor end points , that is where ProxyChains comes in handy . You can chain one or more public proxy server (anonymous as well) to your Tor service .

Port scanning through Tor is very slow , so I will have to find a more scalable solution when it comes to perform this kind of tests in bulk


Python 3.x – Using sets to parse log data

Testing problem: 

As a output of a data transformation program, I had a large excel sheet ( 100 ~ 200 MB) of error logs to sieve through manually to look for error codes.

These error codes were supposed to be compared against an expected set of error codes, to ensure that the program was capturing the complete set of errors ( that were purposely injected into the source data set).

Scripting opportunity: 

I was executing the check “manually” i.e. filtering the output logs to look for the “error_code” column and then retrieve unique error codes to be compared against the source list of unique error codes.

Capture

This has a fair amount of duplicate effort on each test run, hence I decide to script it using my programming language of choice i.e. Python

Scripted comparison approach:

My approach was to iteratively script the test i.e.

  • Script the excel parsing and comparison of error codes
  • Then script the running of the data transformation program ( to output the log files). (This already existed , all I had to integrate this with the parsing/comparison script once I had created it. This post covers the parsing and comparison solution)

Parsing -> I started researching (aka googling) a solution and ended up the with using pyexcel as the module that I will use to parse the excel sheet, mainly because it supports multiple excel formats and has excellent documentation .

Comparison -> This lead to thinking about the second part of the problem i.e. how to retrieve unique error codes from the logs and compare them against an expected list.

I landed on using sets for comparison, as they are an extremely handy to deal with data-sets formed of unique elements and can operate seamlessly with lists & dictionaries.

Equipped with the above tools, I started coding a basic POC as below

Solution -> 


import pyexcel as pe
from datetime import datetime
from datetime import date
import pytest
def test_monthly_close_off_checks():
# this is the expected set of errors from business requirements
expected_error_code_set = set({'J01','J01.N','J02','J03','A01','A02','A02.N','A03','A04','A05','D01','D02','AV01','AV02','AR01','AR02','AP01','AP02','AP03','AP04','AP05'})
#get today's date in YYYYMMDD format as it is appended at the end of the excel sheet that needs to be parsed
today_date = str(date.today().strftime("%Y%m%d"))
print(today_date)
# using pyexcel object parse the first log file to get a list of ordered dicts for each row in the log file
notifications = pe.get_records(file_name = r"CloseOffChecks_Sunjeet\Notifications_"+today_date+".xlsx")
# define an empty set to store the list of unique error codes parsed from the log files
parsed_error_code = set()
#iterate through the rows and get the value of the error code , it is under the column "Error Code" i.e. would be the key in the retrieved dict
for n in notifications:
#add the error to the set . The set will ensure uniquness !
parsed_error_code.add(n["Error Code"])
print (n)
print(parsed_error_code)
#same drill as the notifications file above for the error file
errors = pe.get_records(file_name = r"CloseOffChecks_Sunjeet\Errors_"+today_date+".xlsx")
for e in errors:
#append further error codes to the existing set
parsed_error_code.add(e["Error Code"])
print (n)
print(parsed_error_code)
#assert that the parsed set of error codes is the same as the expected set
assert(parsed_error_code == expected_error_code_set)

view raw

set.py

hosted with ❤ by GitHub

Further work ->

  • Integration with the data transformation program to complete an E2E solution that grabs source data, transforms it , parses and compares error codes
  • Performance ! I am working with fairly chunky log data, how could I optimize my code ?

 

 

 

 

Performing sorting on sub strings in Python 3.x using “key” parameter

sorted() and list.sort() are very useful inbuilt Python functions .

They get even more powerful with the “key” parameter

The key parameter basically allows us to call either another function or some logic, the outcome of which forms the basis of our sorting.

The return value of the key parameter will be used to decide the sort order.

Lets through talk an actual example – 

Lets say we are given an input list of names of great personalities from history and we want to sort the names based on the last name.


input_list = [ 'Sachin Tendulkar','Nelson Mandela', 'Mohandas Ghandhi','Napolean Bonaparte']
output_list = ['Napolean Bonaparte','Mohandas Ghandhi','Nelson Mandela', 'Sachin Tendulkar']

view raw

key.py

hosted with ❤ by GitHub

Step 1:

Write the logic to decide the sort order i.e. sorting based on last name

I wrote a tiny function that will receive a list item and shall return the last name , after splitting the full name string


def last_name(x):
return x.split()[1]

view raw

split.py

hosted with ❤ by GitHub

Step 2:

Use the sorting logic as a key parameter in the sorted() function now.


# just call the function's name as the key value !
output_list = sorted(input_list,key= last_name)

view raw

sorted.py

hosted with ❤ by GitHub

It is as simple as calling the last_name function name as the key and the list will be sorted based on that key’s value.The key’s value acts as a sort of proxy to decide the sorting order .

Bonus learning – 

Rather than defining and calling the key logic as a separate function, we can also use Lambda operator to define the key inline.


# use lambda operator to define the key
output_list = sorted(output_list,key= lambda x: x.split()[1])

view raw

lambda.py

hosted with ❤ by GitHub

The flexi-ways of asserting with Cypress.io

 

One the many joys of working with Cypress is the variety of support for various assertion methodologies.

What is even more powerful is that they can be chained at the end of core Cypress API commands like cy.get

Here are coupe of examples that I put into practice recently.

  1. JQuery based


cy.get("#header > div > div > div:nth-child(2) > div > div.headButtons > div.header_button_float.logMenu > div > a.underLine")
// assert that the element's text matches a reg ex
.should(($txt) => {
const text = $txt.text()
expect(text).to.match(/Login/)
})

view raw

cypress.js

hosted with ❤ by GitHub

2. BDD type assertions


//assert that the element contains a particular text
cy.get('.whtLink').should('contain','nirvana')

view raw

cypres.js

hosted with ❤ by GitHub


//find and confirm that an element is visible
cy.get("#header > div > div > div:nth-child(2) > div > div.headButtons > div.header_button_float.logMenu > div > a.underLine")
.should('be.visible')

view raw

cypress.js

hosted with ❤ by GitHub

Simple , elegant and flexible 🙂

I will continue to practice further ways to assert using Cypress

Which assertion methodology do you particularly prefer?

 

 

Basic refactoring to use PageObjects with Appium (using .NET)

What is PageObject model(POM) ?

POM is a technique to hide some of the details of interacting with Web & Mobile elements, so that writing tests and reading code is easier.

Here is a good starter resource for understanding POM for Appium – https://blog.testproject.io/2017/07/16/page-object-model/

POM in action – 

I want to use this blog post to show a before vs after comparison of using POM , thus highlighting the effectivness of it ( in terms of achieving code abstraction) and hence reducing the cognitive load on someone writing the test .

Before state –

I wrote couple of tests using NUnit to loginto the TradeMe Android app and confirm that the expected user name is being displayed (on successful logon).

Here are the guts of the test –


[Test()]
public void TradeMe_verify_logged_in()
{
// Find the login input field
AppiumWebElement TradeMe_Login = app.FindElementById("nz.co.trademe.trademe:id/editTextLoginEmail");
// key in your user name
TradeMe_Login.SendKeys("xxxxxxx@gmail.com");
// Find the password field
AppiumWebElement TradeMe_Pwd = app.FindElementById("nz.co.trademe.trademe:id/editTextLoginPassword");
// key in the password
TradeMe_Pwd.SendKeys("xxxxxxxx");
// Find the login button
AppiumWebElement Login_Button = app.FindElementById("nz.co.trademe.trademe:id/buttonLogin");
// Tap the login button
Login_Button.Tap(1, 1);
// Wait for the account menu item to be displayed
WebDriverWait wait = new WebDriverWait(app, TimeSpan.FromSeconds(30));
wait.Until(ExpectedConditions.VisibilityOfAllElementsLocatedBy(By.Id("nz.co.trademe.trademe:id/menuAccount")));
// Find icon for my account
AppiumWebElement account_icon = app.FindElementByAccessibilityId("Account");
// tap the icon
account_icon.Tap(1, 1);
// confirm that the logout button is visible, this is in turn ensure that the user name isbeing displayed
wait.Until(ExpectedConditions.VisibilityOfAllElementsLocatedBy(By.Id("nz.co.trademe.trademe:id/buttonLogout")));
// find and assert on the text of the user name
AppiumWebElement account_name = app.FindElementById("nz.co.trademe.trademe:id/textViewAboutMemberUsername");
Assert.AreEqual(account_name.Text, "xxxxxx");
}

After state –


[Test()]
public void TradeMe_verify_logged_in_POM()
{
// Use a Page Object method to enter user name
Trade_Me_PageObject.Enter_UserName("xxxxxx@gmail.com");
// Use a Page Object method to enter password
Trade_Me_PageObject.Enter_Password("xxxxxx");
// Use a Page Object method to press log in button
Trade_Me_PageObject.Tap_LoginButton();
// explicit wait .. this still needs to be refactored 😉
WebDriverWait wait = new WebDriverWait(app, TimeSpan.FromSeconds(30));
wait.Until(ExpectedConditions.VisibilityOfAllElementsLocatedBy(By.Id("nz.co.trademe.trademe:id/menuAccount")));
// Use a Page Object method to click account icon
Trade_Me_PageObject.Tap_AccountIcon();
// another explicit wait… this needs to be refactored too
wait.Until(ExpectedConditions.VisibilityOfAllElementsLocatedBy(By.Id("nz.co.trademe.trademe:id/buttonLogout")));
// perform an assert on the account name returned by the Page Object class
Assert.AreEqual(Trade_Me_PageObject.Return_AccountName_UnderTest(), "sunjeet81_1");
}

view raw

Page.cs

hosted with ❤ by GitHub

Key difference –

In the after state all the locator related code has been abstracted away into the Page Object class.

So, rather than saying

Find me this element and type some text in it ….


AppiumWebElement TradeMe_Login = app.FindElementById("nz.co.trademe.trademe:id/editTextLoginEmail");
TradeMe_Login.SendKeys("xxxxxx@gmail.com");

view raw

1.cs

hosted with ❤ by GitHub

We are just calling a method that does that for us , with worrying about which locator/element to call


Trade_Me_PageObject.Enter_UserName("xxxxxx@gmail.com");

view raw

2.cs

hosted with ❤ by GitHub

Imagine this being repeated for each element and it’s operation across your tests, makes for significant gains in terms of code readibility

How did I abstract the detail out ?

I did some background reading

here –> https://github.com/appium/appium-dotnet-driver/wiki/Page-objects

and looked a some code samples here —> https://github.com/appium/sample-code/tree/master/sample-code/examples/dotnet/AppiumDotNetSample

All the element locator related info was moved to another class. Operations (e.g. entering text or getting text of an element) were exposed as methods by that class.


public class Trade_Me_logon
{
// Define the element and how will we find it ?
[FindsBy(How = How.Id, Using = "nz.co.trademe.trademe:id/editTextLoginEmail")]
IWebElement LoginField { get; set; }
// Do some action on the element
public void Enter_UserName(string text)
{
LoginField.SendKeys(text);
}

view raw

PageObject.cs

hosted with ❤ by GitHub

The above methods and elements were then consumed by the NUnit test


Trade_Me_PageObject.Enter_UserName("xxxxxx@gmail.com");

view raw

consume.cs

hosted with ❤ by GitHub

To Do/Debt –

  1. As you might have observed in one of the code snippets above,I have to still refactor the explicit wait related code . Need to do some research on how to do it 😉
  2.  For the same element, I am using  Interface “IWebElement” in the Page Object class vs “AppiumWebElement” in my original NUnit test . The reason I had to do it because using “AppiumWebElement” type in the Page Object class used to throw an exception when called from the test. I still have to figure out why !?

 

 

 

 

 

 

 

 

 

Real device testing with TestObject,Appium and C#

TestObject , an offering by Sauce Labs provides cloud testing service with real devices.

I recently did a POC with TestObject using C# . The tests were run using NUnit.

Step 1:  Sign up

Sign up for a free TestObject account 

Step 2:  Upload app

Upload the .apk/.ipa file for app under test . I used a free basic notepad Android app

Step 3: Understand which capabilities need to be set 

TestObject have a decent amount of documentation on their website.

There is a Java based example here, that I translated to C# . This example calls out a set a typical capabilities required to call an actual device on the TestObject cloud

Here is the my code :


capabilities.SetCapability("testobject_api_key", "48702A635B794A6FB8D5B852FE62723A");
capabilities.SetCapability("platformName","Android");
capabilities.SetCapability("platformVersion","6.0");
capabilities.SetCapability("appiumVersion", "1.7.2");
capabilities.SetCapability("deviceName", "Motorola_Moto_E_2nd_gen_free");
capabilities.SetCapability("privateDevicesOnly", "false");
capabilities.SetCapability("testobject_app_id", "1");
capabilities.SetCapability("phoneOnly", "false");
capabilities.SetCapability("tabletOnly", "false");
Uri server = new Uri("https://eu1.appium.testobject.com/wd/hub");

view raw

appium.cs

hosted with ❤ by GitHub

You will find the API key and details of the capabilities , by clicking the gears icon (under your TestOBject account) –> selecting Appium —> Set up instructions page

The device details will be in the device list under the “Live Testing”link  –

Lastly , the app id can be retrieved from the “Dashboard”link in TestObject

Step 4: Next is to write some NUnit tests to control the app in TestObject

I used this tutorial for guidance ( as I am a novice with NUnit and C# 🙂 )

Wrote couple of simple tests to valid the context of the app and title of the new note window in my sample Notepad application


using NUnit.Framework;
using System;
using OpenQA.Selenium.Appium.Android;
using OpenQA.Selenium.Remote;
using OpenQA.Selenium.Appium;
using System.Net;
using OpenQA.Selenium;
using System.Drawing.Imaging;
using System.Threading;
namespace AppiumBasicSetup
{
[TestFixture()]
public class BasicTest
{
AndroidDriver<AppiumWebElement> driver;
//AndroidDriver<IWebElement> driver;
[SetUp()]
public void SetUp()
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
Console.Write("Setting up the test");
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.SetCapability("testobject_api_key", "xxxxxxxxxxxx");
capabilities.SetCapability("platformName","Android");
capabilities.SetCapability("platformVersion","6.0");
capabilities.SetCapability("appiumVersion", "1.7.2");
capabilities.SetCapability("deviceName", "Motorola_Moto_E_2nd_gen_free");
capabilities.SetCapability("privateDevicesOnly", "false");
capabilities.SetCapability("testobject_app_id", "1");
capabilities.SetCapability("phoneOnly", "false");
capabilities.SetCapability("tabletOnly", "false");
Uri server = new Uri("https://eu1.appium.testobject.com/wd/hub");
TimeSpan time_out = TimeSpan.FromMinutes(2);
driver = new AndroidDriver<AppiumWebElement>(server, capabilities,time_out);
driver.Manage().Timeouts().ImplicitlyWait(time_out);
}
[Test()]
public void App_should_have_context_Test()
{
Assert.IsNotNull(driver.Context);
}
public AppiumWebElement Create_a_NewNote()
{
AppiumWebElement new_note = driver.FindElementByAccessibilityId("New note");
new_note.Tap(1,1);
AppiumWebElement new_note_screen = driver.FindElementById("android:id/action_bar_title");
return new_note_screen;
}
[Test()]
public void Verify_Title_of_NewNote_Test()
{
AppiumWebElement new_note_screen1 = Create_a_NewNote();
String new_note_title = new_note_screen1.Text;
driver.GetScreenshot();
Assert.AreEqual(new_note_title, "New note1");
}
[TearDown()]
public void TearDown()
{
driver.Quit();
Console.Write("Tearing down the test");
}
}
}

view raw

appium2.cs

hosted with ❤ by GitHub

Step 5: Running the tests in Visual Studio and checking results in TestObject

Kick off the NUnit test in Visual studio and then head over to your TestObject account , click on the gears icon and you would see activity under the list of Appium tests ( hopefully) to reflect your test running

Clicking on the test run link , you can view the Appium logs, Device logs, any screen shots taken and a nice video recording .

The usual Sauce Labs stuff , but on an actual device 🙂

Happy testing !