RESTful Web Services with Powershell

Working with a client recently and needed to check connectivity to a web service.  I wanted to make a few HTTP requests. Normally I’d fire up curl and paste a URL with basic auth credentials:

curl -u $USERNAME:$PASSWORD -k https://api.example.com/some/endpoint

But… they were on Windows. No problem, fire up chocolately and install curl

choco install curl

Ah… this server is locked down and no way they’re going to fill out the paperwork (in triplicate) to install .  Someone said that PowerShell has curl installed.  But wait a minute…

Screen Shot 2017-08-18 at 3.21.40 PM

Someone has played a nasty trick on me.

I can’t use curl, but PowerShell has something not quite, but almost entirely unlike curl.  Let’s see what we can do with Invoke-WebRequest.

Ok, so you can call a URL with:

Invoke-WebRequest -Uri https://api.example.com/some/endpoint

It turns out that passing Basic Authentication credentials is trickier. But wait! stackoverflow comes to the rescue(-ish).

You have a -Credential flag, but all that does is open up a Windows dialog to enter them.  Not very automatable. (automatible? odd-tomato-bull?)

Basic authentication is really just an HTTP header:

GET /some/endpoint HTTP/1.1
Authorization: Basic SSBsb3ZlIEtlbHNleSBGb3g=

And the credentials are just a base64 encoded string containing text in the format

username:password

One last thing though, you need to escape the colon in your credentials.  Apparently backtick is the escape character in Powershell:

$credentials = "$username`:$password"

And you can pass an array of headers with Invoke-WebRequest.   (backtick also allows continuation on the next line)

Invoke-WebRequest `
-Uri https://api.example.com/some/endpoint `
-Headers @{ Authorization = "Basic $encodedCredentials" }

So now I’m getting somewhere.

The only step left is to figure out how to Base64Encode with PowerShell.  I’m not sure where I found it, but this will do the trick:

$encodedCredentials = `
[System.Convert]::ToBase64String( `
[System.Text.Encoding]::ASCII.GetBytes($credentials))

Remember folks, only you can prevent github from sharing your credentials.  Use an environment variable, and don’t put your password in powershell your script:

Here’s how you get an environment variable in Powershell

Get-ChildItem Env:USERNAME

Finally, here is my full script:

$username = $(Get-ChildItem Env:USERNAME).value
$password = $(Get-ChildItem Env:PASSWORD).value

$credentials = "$username`:$password"
$encodedCredentials = `
[System.Convert]::ToBase64String( `
[System.Text.Encoding]::ASCII.GetBytes($credentials))

Invoke-WebRequest `
-Uri https://api.example.com/some/endpoint `
-Headers @{ Authorization = "Basic $encodedCredentials" }

But wait, there’s more!

Now that I’ve got my response I can actually use Powershell to parse the JSON response and access it using ConvertFrom-Json.:

Given the following JSON

[{ "contact": { "name": "Aaron Evans", "website": { "url": "http://one-shore.com" }, "blog": { "url": "https://fijiaaron.wordpress.com" } }}]

I could parse it like this:

$response = $(Invoke-WebRequest -Uri "$endpoint" -Headers $headers)
$contacts = $(ConvertFrom-Json -InputObject $response.Content)

Invoke-WebRequest -Uri  $contacts[0].contact.blog.url

Please don’t flood my blog with hits.  I haven’t monetized it yet.

See the whole thing with this gist:

 

 

 

Advertisements

Disable Integrated Windows Authentication (IWA) for Selenium

Here’s the problem:

You have a site that you’d like to write automated tests for.  But when you attempt to login with Internet Explorer, it has a Windows Authentication dialog popup. Because this is a native UI element, Selenium can’t touch it.

If you have control of the machine (and the Windows ADFS domain controller), you can add the user and the popup will go away.  But if you don’t have control — like if you’re running your tests on a virtual machine in the cloud with Sauce Labs — you need to find a way to get around it.

I recently ran into this problem.  It only affected Internet Explorer.  Chrome and Firefox were directed to a web login form — which could be automated with Selenium.  The options were to either A) figure out how to authenticate with Windows, or B) bypass Windows Authentication so it doesn’t have the native popup, and it doesn’t block tests.

I think I’ve found a solution.

You can disable Integrated Windows Authentication under “Internet Options” for Internet Explorer.  Under the “Advanced” tab, scroll down to “Security” and uncheck “Enable Integrated Windows Authentication”.  That should do it.

 

 

But there was still the task of automating this step.  I thought of trying a macro tool like AutoIt, but that just didn’t feel right.  Plus, it can be tricky to actually use AutoIt.  I don’t want to learn their language, and the recorder has been removed from AutoIt.

Click here to see how to get an older version of AutoIt that still has a working recorder:

https://www.autoitscript.com/forum/topic/176009-where-is-au3recordexe/

I knew there must be some way to do this without resorting to UI automation.  So, after a little poking around, I found this old blog post:

https://laxmanchip.wordpress.com/2011/12/16/enabledisable-integrated-windows-authentication-vbscript/

Thanks Tom!

A little more searching taught me all I need to know about using Powershell to edit registry keys:

https://blogs.technet.microsoft.com/heyscriptingguy/2015/04/02/update-or-add-registry-key-value-with-powershell/

Combining the two gave me this nice little script:

Set-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings" -name EnableNegotiate -value 0
Get-Item "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings"

 

And now I can execute that on a remote computer before running my Selenium automation!

Sauce Labs has the following in their wiki about using AutoIt as a pre-run executable:

https://wiki.saucelabs.com/display/DOCS/Running+an+AutoIt+Script+as+a+Pre-run+Executable+to+Handle+Windows+Security+Authentication+Dialogs

And some general information about specifying a pre-run executable in your Desired Capabilities.

Thanks Sauce Labs!

And thanks to the team that set up this challenge — you know who you are ;)

 

 

Weekly Wednesday Webinar on Selenium & Sauce Labs

I’ve been working at Sauce Labs for a while now, helping enterprise users build test automation frameworks and implement continuous integration using Selenium & Sauce Labs.

In order to reach a larger audience — and to learn more about people’s challenges developing test automation — I’m going to be hosting a weekly webinar on using Selenium with Sauce Labs for test automation.

So, starting this week, each Wednesday during lunch (12:30pm Mountain Time) I’ll host a webinar / office hours.  I’ll begin with a brief presentation introducing the topic, followed by a demo (live coding — what could go wrong?), and then open it up for questions & comments.

The first webinar will be tomorrow at 12:30pm MST.  The topic is DesiredCapabilities.

I’ll talk about what desired capabilities are, how to use desired capabilities with Sauce Labs, and show how you can use the  Sauce Labs platform configurator to generate desired capabilities.  I’ll also talk about Sauce Labs specific capabilities used to report on tests and builds.

Register on EventBrite here: Selenium & Sauce Labs Webinar: Desired Capabilities

Join the WebEx directly: Selenium & Sauce Labs Webinar: Desired Capabilities

Contact me if you’d like a calendar invite or more info.

Testing PDF downloads with Sauce Labs

There are 2 main challenges to testing PDF downloads with Sauce Labs:

1. Testing PDFs in the browser are not supported by Selenium.
2. Download dialogs are native UI elements and not supported by Selenium.

A PDF file may try to open in a PDF plugin, which cannot be accessed by Selenium. But, even if you change your browser configuration to download PDF files automatically, as described here:

http://elementalselenium.com/tips/2-download-a-file

Sauce Labs does not provide access to the file system on our VMs. So, in order to test a PDF you would need to download it locally. The best practice is to get the URL of the link using Selenium and then to download it using wget, curl, or your favorite HTTP library.

See this blog post which goes into further detail:

http://ardesco.lazerycode.com/index.php/2012/07/how-to-download-files-with-selenium-and-why-you-shouldnt/

Testing mobile apps in the cloud

There are several services available for testing mobile apps in the cloud on real devices:

Sauce Labs

Sauce Labs is the veteran and was co-founded by Jason Huggins, the original creator of Selenium.  Sauce Labs also works on development of Appium.

BrowserStack

BrowserStack is an economical choice for testing in the cloud, but while the offer “Live” mobile devices (manual testing only), it does not appear that they offer devices for test automation.

Xamarin Test Cloud

Xamarin is a pretty good contender, but focuses their tooling around Microsoft .NET / Visual Studio developers.  It does not appear that Appium is supported (directly) Still, they have a big selection of devices for those willing to stick to Microsoft tooling. It is also nice that their metering is a soft limit.

Perfecto Mobile

Perfecto Mobile has been around for a while as a service for Enterprises.  It is proprietary, but also supports Appium.  It comes with an Enterprise level price though.  You have to call and talk to a salesperson for any plan beyond proof of concept.

Keynote Mobile Testing

Keynote Mobile Testing used to be called Device Anywhere and their experience dates back to the days before smart phones.  I don’t have recent experience with them, but they used to offer only pixel based automation.  It appears they support Appium now.  But automation is not supported outside their enterprise sales channel.

Amazon Device Farm

Amazon Device Farm is the most recent entry in the field.  But it is not only quite expensive, but requires a lot of do-it-yourself.  Perhaps it will be more practical once tooling for recipes becomes available.

 

Price Comparision of Sauce Labs, Xamarin Test Cloud, Perfecto Mobile, & Amazon Device Cloud

Service Price Device time Concurrent Devices
Sauce Labs $129/month* 1000 minutes 4
$259/month* 2000 minutes 4
399/month* 3000 minutes 6
$499/month* 4000 minutes 8
Xamarin Test Cloud $99/month** 1 hour/day 1
$379/month** 5 hours/day 3
$799/month** 10 hours/day 5
Perfecto Mobile $399/month* 20 hours 1
Amazon Device Farm $0.17/minute 1
Amazon Device Farm $250/month unlimited 1

* 25% annual discount available

** 15% annual discount available

Setting system properties with Gradle

In Java you can pass system properties from the command line like this:

java -D MyProperty=foo MyClass

And you can then get them in your code like so:

public class MyClass {
  public String getMyProperty() {
    return System.getProperty("MyProperty");
  }
}

Pretty easy, no?

You can pass system arguments the same way with Gradle:

gradle -D MyProperty=foo run

But what if you want to manipulate or use those properties in Gradle first?

Instead of -D you can use -P to pass properties to the Gradle object, and then you can do whatever you want with it.

gradle -P MyProperty=foo MyClass

And then you can use your properties in Gradle and then pass them to the System properties thusly:

task setProperty << {

    if (project.hasProperty("ENV")) {
        println "project has property ENV"
        System.properties["ENV"] = "$ENV"
    } else {
        println "project does not have property ENV"
        System.properties["ENV"] = "dev"
    }

    println "ENV: " + System.properties["ENV"]
}

 

Some references:

https://docs.gradle.org/current/userguide/writing_build_scripts.html#N10FDD

https://docs.gradle.org/current/userguide/build_environment.html

http://mrhaki.blogspot.com/2010/10/gradle-goodness-pass-command-line.html

http://mrhaki.blogspot.com/2010/09/gradle-goodness-different-ways-to-set.html