Categories
Automated Acceptance Testing Automated Testing

UI automation of vendor delivered products always leads to trouble

wise_old_elfI’ve got three darling boys, and they love this show on ABC4Kids called ‘Ben & Holly’s Little Kingdom’. It’s a cartoon from the makers of Peppa Pig about tiny elves and fairies and there’s a character named the wise old elf (pictured) who doesn’t like the fairy magic and whose catchphrase is ‘magic always leads to trouble‘ which has become a little bit of a meme in our household where we replace the word ‘magic’ with something else that’s perilous. This leads me to the point of this article, something I have a strong opinion about:

“UI automation of vendor delivered products always leads to trouble”

Why do I believe that? To be successful in UI automation involves some critical elements which are missing when writing automated tests against a black box vendor delivered products (such as a customized CRM solution). To be successful in UI automation:

  • you need collaboration between testers and developers to write the code needed to write robust and efficient user interface tests;
  • you need opportunities to include testability into the user interface, whether this be test specific navigation controllers, or ensuring that page elements are appropriately identified and structured; and
  • you need to be able to identify areas which can be tested below the UI, whether this be through APIs and web services, or hitting the database directly. Vendors seldom provide services and almost never allow direct access to the database – particularly if it’s a SaaS product.

I strive to advise anyone that it’s a bad idea to write automated tests against the UI of a vendor delivered product. Either that vendor should be doing their own automated testing, or be providing a more robust way to automatically test that changes have been correctly applied to their product.

I also try to avoid any career opportunities that put me in a situation where this is required of me because I don’t believe in it and haven’t seen it been successfully done.

As the wise old elf says: “UI automation of vendor delivered products always leads to trouble”.

Categories
Appium Automated Acceptance Testing Automated Testing iOS ruby Selenium WebDriver

Using appium in Ruby for iOS automated functional testing

As previously explained, I recently started on an iOS project and have spent a bit of time comparing iOS automation tools and chose Appium as the superior tool.

The things I really like about Appium is that it is language/framework agnostic as it uses the WebDriver standard WIRE protocol, it doesn’t require any modifications to your app, supports testing web views (also known as hybrid apps) and it supports Android since we are concurrently developing an Android application (it also supports OSX and Firefox OS but we aren’t developing for those, yet). There isn’t another iOS automated testing tool, that I know of, that ticks that many boxes for me.

Getting Started

The first thing to do is download the appium.app package from the appium website. I had an issue with the latest version (0.11.2) launching the server which can be resolved by opening the preferences and checking “Override existing sessions”.

You run the server from inside the appium.app which takes your commands and relays them to the iOS simulator. There’s also a very neat ‘inspector’ tool which shows you all the information you need to know about your app and how to identify elements.

Note: there’s currently a problem with XCode 5.0.1 (the latest version as I write) which means Instruments/UIAutomation won’t work at all. You’ll need to downgrade (uninstall/reinstall) to XCode 5.0 to get appium to work at all.

Two Ruby Approaches

This confused me a little to start, but there’s actually two vastly different ways to use appium in ruby.

1) Use the standard selenium-webdriver gem

If you’re used to using WebDriver, like me, this will be the most straightforward approach (this is the approach I have taken). Appium extends the API to add different gestures by calling execute_script from the driver, so all other commands stay the same (for example, find_element).

2) Use the appium_lib library

There is a Ruby gem appium_lib that has a different API to the selenium-webdriver gem to control appium. I don’t see any massive benefits to this approach besides having an API that is more specific to app testing.

Using Selenium-WebDriver to start appium in ruby

Launching an appium app is as simple as defining some capabilities with a path to your .app file you have generated using XCode (this gets put into a deep folder so you can write the location to a file and read it from that file).

capabilities = {
'browserName' => 'iOS',
'platform' => 'Mac',
'version' => '6.1',
'app' => appPath
}
driver = Selenium::WebDriver.for :remote,
desired_capabilities: capabilities,
url: "http://127.0.0.1:4723/wd/hub"

Locating elements

Once you’ve launched your app, you’ll be able to use the appium inspector to see element attributes you can use in appium. Name is a common attribute, and if you find that it’s not being shown, you can add a property AccessibilityIdentifier in your Objective C view code which will flow throw to appium. This makes for much more robust tests than relying on labels or xpath expressions.

driver.find_element(:name, "ourMap").displayed?

Enabling location services for appium testing

This got me stuck for a while as there’s quite a bit of conflicting information about appium on how to handle the location services dialog. Whilst you should be able to interact with it as a normal dialog in the latest version of appium, I would rather not see it at all, so I wrote a method to copy a plist file with location services enabled in it to the simulator at the beginning of the test run. It’s quite simple (you can manually copy the clients.plist after manually enabling location services):

def copy_location_services_authentication_to_sim
source = "#{File.expand_path(File.dirname(__FILE__))}/clients.plist"
destination = "#{File.expand_path('~')}/Library/Application Support/iPhone Simulator/7.0/Library/Caches/locationd"
FileUtils.cp_r(source, destination, :remove_destination => true)
end

Waiting during appium tests

This is exactly the same as selenium-webdriver. There’s an implicit wait, or you can explicitly wait like such:

driver.manage.timeouts.implicit_wait = 10
wait = Selenium::WebDriver::Wait.new :timeout => 30
wait.until {driver.find_element(:name, 'monkeys').displayed? }

Mobile gestures

The obvious difference between a desktop web browser and a mobile app is gestures. Appium adds gestures to WebDriver using execute_script. I recommend using the percentage method (0.5 etc) instead of pixel method as it is more resilient to UI change.

For example:

driver.execute_script 'mobile: tap', :x => 0.5, :y => 0.5

or

b = driver.find_element :name, 'Sign In'
driver.execute_script 'mobile: tap', :element => b.ref

Testing Embedded Web Views

The native and web views seamlessly combine so you can use the same find_element method to find either. The appium.app inspector displays the appropriate attributes.

Note: I can’t seem to be able to execute a gesture (eg. swipe) over a Web View. I don’t know whether this is a bug or a limitation of Appium.

Summary

I have found that using the familiar selenium-webdriver gem with appium has been very powerful and efficient. Being able to open an interactive prompt (pry or irb) and explore your app using the selenium-webdriver library and the appium.app inspector is very powerful as you can script on the fly. Whilst appium still seems relatively immature, it seems a very promising approach to iOS automation.

Now to get watir-webdriver to work with appium.

Categories
Android Automated Acceptance Testing Automated Testing Cucumber iOS OSX

The current state of iOS automated functional testing

I’ve been working on an iOS project and have been looking for a suitable automated functional test library to drive the iOS GUI. I must say that automated iOS testing feels like automated web testing did about 10 years ago: lots of different tools taking different approaches, and all approaches quite flaky!

Through research I came across quite a few different tools that support iOS automated functional testing but need to work out which one is best.

Whilst I often advocate writing functional tests in the same language as your codebase, in the case of iOS and Objective C, I think using a more lightweight language like Ruby has its own advantages (I don’t really like Objective C).

The one thing I really dislike with a lot of these iOS functional test tools is how they are married to Cucumber and encourage users to write tests like “I tap button x” and “I scroll through list y”. These types of tests are much harder to read as they express implementation over intention and should be avoided.

I will also be writing tests for an Android app so being able to use the same tool-set is a great advantage to me.

I also personally prefer an approach where I don’t need to modify the core behavior of my app to run tests against it (designing for testability is completely different and vital). Some approaches embed some sort of server to receive automation commands, which the user must then remove later because it uses undocumented Apple APIs which will lead to app store rejection. Being able to run tests against the app that you submit to the app store means you can be more confident you have tested it right.

Finally, the iOS app I am working on using embedded web views to display dynamic content from the server, which can be interacted with, and therefore it is vital that these can be interacted with. This feature is actually very rare in an iOS automation framework.

Here’s the list of iOS functional automation tools I am aware of and how they stack up:

  • Tool: Frank
    • Language: Ruby
    • Test Framework: Also supports OSX apps
    • Supports other mobile plaforms: Also supports OSX apps
    • Approach: Requires you to embed a symbiote server in your app and uses undocumented APIs
    • Deploy same app to store: NO
    • Supports testing web-views?: NO (only via JavaScript calls)
  • Tool: KIF
    • Language: Objective C
    • Test Framework: OCUnit/SenTest
    • Supports other mobile plaforms: NO
    • Approach: Modifies your app to use undocumented APIs
    • Deploy same app to store: NO
    • Supports testing web-views?: NO
  • Tool: Subliminal
    • Language: Objective C
    • Test Framework: OCUnit/SenTest
    • Supports other mobile plaforms: NO
    • Approach: Embeds into your project but uses UIAutomation instead of undocumented APIs
    • Deploy same app to store: NO
    • Supports testing web-views?: NO
  • Tool: Zucchini
    • Language: Custom DSL (CoffeeScript/Ruby)
    • Test Framework: Custom
    • Supports other mobile plaforms:
    • Approach: Generates UIAutomation JavaScript that is executed against your app
    • Deploy same app to store: YES
    • Supports testing web-views?: NO
  • Tool: Calabash
    • Language: Ruby
    • Test Framework: Cucumber
    • Supports other mobile plaforms: Also supports Android Apps
    • Approach: Requires you to embed a server in your app to control it
    • Deploy same app to store: NO
    • Supports testing web-views?: YES
  • Tool: Appium
    • Language: Ruby, C#, Java, JavaScript, Objective C, PHP, Python, Perl, Clojure
    • Test Framework: Agnostic
    • Supports other mobile plaforms: Also supports Android
    • Approach: Uses Instruments to control app using the standard WebDriver WIRE protocol
    • Deploy same app to store: YES
    • Supports testing web-views?: YES
  • Tool: ios-driver
    • Language: Ruby, C#, Java, JavaScript, Objective C, PHP, Python, Perl, Clojure
    • Test Framework: Agnostic
    • Supports other mobile plaforms: NO
    • Approach: Uses Instruments to control app using the standard WebDriver WIRE protocol
    • Deploy same app to store: YES
    • Supports testing web-views?: YES

I’ll let you guess which tool I selected until I write my next blog post about how to get started in that tool.

Categories
Automated Testing Presentations

Minesweeper in Melbourne

I did a presentation on Einstein the Minesweeper robot at an ANZTB SIGIST in Melbourne this morning. The facilitator of the session was ill so I was asked to host the event as well, so I felt very much under pressure.

I came up with an idea for a quick activity to “awaken our senses” as it was early in the morning where I asked everyone to count how many human senses they are aware of. Most people gave the traditional response of five (or six), and I challenged their views by presenting some additional senses we humans possess. Check out the Wikipedia article if you’re interested to know more.

Unfortunately the first presentation on Context Driven Testing by Mark Richards went well over the allocated time, so I had to rush my presentation to ensure I could catch my 11am flight home: I made it, just.

Being for a group for testers, my intention was to not focus on the detail of the actual Minesweeper robot but what solving a complex problem such as automating Minesweeper can teach use about testing and how to be better testers.

Click the slide below to view the slideshow.

Melbourne SIGIST Einstein_ The Minesweeper Robot

Categories
ATDD Automated Acceptance Testing Automated Testing Cucumber Specflow

Avoid using case statements in your cucumber/specflow/jbehave step definitions

I quite frequently come across a scenario that looks something like this:

Scenario: Create some animals
  Given I am a zoo keeper
  When I create a giraffe
  And I create a lion
  And I create a pony
  And I create a unicorn
  Then I should have a zoo

and step definitions that implement the When steps with a single step definition:

When /^I create a (\D+)$/ do |animal|
  case animal
    when 'lion'
      create_a_lion()
    when 'giraffe'
      create_a_giraffe()
    when 'pony'
      create_a_pony()
    else
      raise 'Unknown animal'
  end
end

I don’t like having case statements in steps for a number of reasons:

  • For readability and maintainability reasons I try to keep my step definitions as short as possible (usually a couple of lines), and using a case statement violates this principle;
  • Raising an exception to catch invalid usage of the step (in an else clause) replicates what these BDD frameworks already do, provide feedback about unimplemented steps;
  • IDEs that support step auto completion (such as RubyMine & Visual Studio) will not suggest valid steps as they don’t understand how you’ve implemented a case statement; and
  • If used inappropriately (such as our unicorn step), the test will only fail at run-time whereas most IDEs will highlight non-matching steps as you’re writing.

For example, you could change our steps to look something like this:

When /^I create a lion$/ do
  create_a_lion()
end

When /^I create a giraffe$/ do
  create_a_giraffe()
end

When /^I create a pony/ do
  create_a_pony()
end

Even though this is three times as many step definitions, it is actually less code (9 lines compared to 12).

By using this approach it is obvious we can’t currently create a unicorn as RubyMine tells us before we even run our tests. And we don’t need to raise any exceptions myself.

rubymine highlights unimplemented steps

Whilst a lot of people use case statements in steps to reduce the number of steps, it is actually counter intuitive as it means you have more code to do so, and the outcome is less usable when writing scenarios. So, please avoid putting case statements in your step definitions.

Categories
Automated Acceptance Testing Automated Testing

Internationalization and Localization Testing

Internationalization vs Localization

Internationalization is the process of making a software application easily adaptable to be used by international audiences. Localization is the process of adding a new locale to a software application.

Internationalization is most often implemented by ensuring that labels and values on screens are not ‘hard-coded’, they are read from a common source to ensure they can be easily switched when running in a different locale. Localization is adding a new source, or locale, so that a new audience can use your application.

I have found the most effective way to test that an application is internationalized is to create a new locale specifically for testing purposes. Your new locale has a specific known formula for translation which you use to check that you application is fully internationalized.

You should then also run all your automated acceptance tests in your new locale to ensure that all new functionality is internationalized as it developed.

I’d recommend either of the following approaches to define a new locale for testing:

Approach Description Welcome’ Translated Pros Cons
Lorem Ipsum A defined, fixed length string Lorem ipsum dolor sit amet’
Can see how short elements overflow
Easy to test
Screens don’t look as realistic
Screens can be hard to understand
Reversal Reversal of the same string emocleW Screens look realistic
Easy to test
Can’t see overflow effects

I prefer the reversal approach as it’s the most realistic representation, and it’s easy to do some exploratory testing on a different ‘real’ locale to spot overflow/formatting issues.

Say you have a screen that looks something like this in English:

English Screen

This same screen in Lorem Ipsum would look something like:

Lorem Screen

And it would look like this in Reverse:

Reverse English Screen

Manually testing internationalization

This is easy. Run your app in whatever test locale you have decided to create. Then ensure that each screen has localized data for each label.

Automatically testing internationalization

You can fairly easy ensure your automated acceptance tests can run against your test locale. You just need to ensure that all labels which are asserted can be translated in your acceptance tests.

For example:

Given I am an anonymous user
When I visit the personal details screen
Then I should see the heading “Please enter your details”
And I should see the label “Name:”
And I should see the label “Email:”
And I should see the label “Phone:”
And I should see the button with label “Continue”

Each of these steps has a specific label that needs to be translated. Instead of asserting the actual value, in your automated test code you first translate the string to your test locale, and then assert that value. Your automated tests will then fail if the actual string appears on the screen untranslated.

Categories
Automated Acceptance Testing Automated Testing

Don’t bury your hooks

A slightly technical post here.

If you’re using a BDD framework such as Specflow or JBehave, can I please ask that you don’t bury your hooks. These frameworks provide hooks, or events, you can use to repeat certain things. An example in Specflow is:

[BeforeScenario]
public static void GoToHomePage()
{
Driver.Navigate().GoToUrl(GoogleUrl);
}

You can put these hooks in any steps class, but please, put them in one place, all together, preferably in a class/file named hooks or the like.

I recently came across a code base where these hooks where spread across lots of different steps files, which makes it very confusing and hard to debug as you don’t know where all this code is being called from when you’re running your tests.

Categories
Automated Testing

Stop Firefox auto-updating (and breaking your CI build)

If you run regular automated WebDriver tests using Firefox then chances are you’ve encountered the problem where Firefox updates itself and your WebDriver bindings don’t support that version which causes all your tests to fail. It’s really annoying, so I suggest you set your Firefox install to never automatically update as below to avoid this happening.

Stop Firefox auto updating

Update:

As Alex points out below, you can do this programmatically for a server you don’t actually launch Firefox on, ie. a headless machine.

profile = Selenium::WebDriver::Firefox::Profile.new
 # disable autoupdate
 profile['app.update.auto'] = false
 profile['app.update.enabled'] = false
Categories
Automated Acceptance Testing Automated Testing

Refactoring legacy code using automated acceptance tests

A question I often get asked as a consultant is how to maintain a legacy code base that has very little or no unit tests, or any automated tests for that matter. How do you start to refactor the code and introduce unit tests?

The problem with immediately adding unit tests to a unit test free legacy code base is the code won’t be testable (as it wasn’t test-driven), and it will require refactoring to make it so. But refactoring legacy code without unit tests as a safety net is risky and can easily stop your application and introduce bugs, so you’re in a catch-22.

A good solution to this problem is to write a few specific automated acceptance tests for the feature you would like to add unit tests to, before you write any unit tests.

For example, imagine you would like to add some unit tests to the registration feature of your application, you might automate four acceptance test scenarios:

Scenario One: User can register using valid credentials
Scenario Two: User can’t register using existing email address
Scenario Three: User can’t register without providing mandatory data
Scenario Four: User can’t register from a non-supported country

Before you make any changes to your code base, you must write and execute these scenarios until they are all passing.

Once they are all passing then you can start writing unit tests one by one until each passes. As you write unit tests and refactor your code as required, you should run your specific acceptance tests to ensure the high level functionality hasn’t been broken by your changes.

Once you have completed your unit tests you will have a set of unit tests and automated regression tests with code that supports both.

You can repeat this for each feature in your system, and slowly combine a few key scenarios into an end-to-end scenario that replicates a common user-journey throughout your application.

At the end of this process you will have cleaner code, a set of unit tests, a set of feature based acceptance tests and one or two end-to-end user journeys that you can execute against any change you make.

Paying down technical debt is hard, but following a methodological approach using automated acceptance tests as a safety net makes it practical to do so.

Categories
Agile Software Dev Automated Acceptance Testing Automated Testing Software Testing

Is test management wrong?

I was somewhat confused by what was meant by the recent article entitled “Test Management is Wrong“. I couldn’t quite work out whether the author meant Test Management (the activity) is wrong, Test Managers (the people) are wrong or Test Management Tools (the things) are wrong, but here’s my view of these three things:

Test Management (the activity): now embedded in agile teams;
Test Managers (the people): on the way out; and
Test Management Tools (the things): gathering dust

Let me explain with an example. Most organizations see the benefit of agile ‘iterative’ development and have or are in the process of restructuring teams to work in this way. A typical transformation looks like this:

Agile Transformation

Instead of having three separate larger ‘analysis’, ‘development’ and ‘test’ teams, the organization may move to four smaller cross functional teams consisting of say one tech lead, one analyst, one tester and four programmers.

Previously a test manager managed the testing process (and testing team) probably using a test management tool such as Quality Centre.

Now, each agile team is responsible for its own quality, the tester advocates quality and encourages activities that build quality in such as accurate acceptance criteria, unit testing, automated acceptance testing, story testing and exploratory testing. These activities aren’t managed in a test management tool, but against each user story in a lightweight story management tool (such as Trello). The tester is responsible for managing his/her own testing.

Business value is defined and measured an iteration at a time by the team.

So what happens to the Analysis, Development and Test Managers in the previous structure? Depending on the size of the organization, there may be a need for a ‘center of excellent’ or ‘community practice’ in each of the areas to ensure that new ideas and approaches are seeded across the cross-functional teams. The Test Manager may be responsible for working with each tester in the teams to ensure this happens. But depending on the organization and the testers, this might not be needed. This is the same for the Analysis Manager, and to a lesser extent, the Development Manager.

Step by Step test cases (such as those in Quality Center) are no longer needed as each user story has acceptance criteria, and each team writes automated acceptance tests written for functionality they develop which acts as both automated regression tests and living documentation.

So the answer the author’s original message: no I don’t think test management is wrong, we just do it in a different way now.