Jeremy Bellows - Using F# and Canopy for UI Testing

Using F# and Canopy for UI Testing

-


F# and Canopy is one of the most comfortable UI testing experiences any developer or quality engineer could ask for. The combined syntax intentionally removes code noise and presents itself as legible. Canopy is an excellent Selenium alternative.

Prerequisites

The development machine in use needs the following dependencies installed

Example

Below is an example of a canopy test

"This is my test description where I describe my hypothesis" &&& fun _ -> //this is a comment.
  start Chrome
  url "http://www.jeremybellows.com"
  displayed "Jeremy Bellows"
  click "Jeremy Bellows"
  on "http://www.jeremybellows.com"
  displayed "Jeremy Bellows"
  quit ()

Explanation of Example

“” &&& fun _ ->

The first line of code registers a test under the description provided and with the test function passed as a parameter.

"This is my test description where I describe my hypothesis" &&& fun _ -> //this is a comment.

Canopy defines the custom operator &&&, which takes two arguments; a string, our test description, and a function, our test code. See the canopy code for &&& here

fun _ -> is syntax for defining an anonymous function that takes one parameter. The symbol _ is used to represent the first parameter because the test doesn’t care about what is passed.

start

start instantiates a canopy defined Browser

See the canopy code for start here

url

url navigates to the supplied url

click

click will click a html element that is found using a css selector, xpath, or text.

click "Jeremy Bellows" will find a html element that contains the text Jeremy Bellows

on

on is an assertion that tests if the current page is the expected url

When an assertion fails in canopy, it throws an exception and is logged by the test runner.

displayed

displayed is an assertion that tests if the html element found using a css selector, xpath, or text is visible.

quit

quit is a function that closes all browsers opened by canopy. I highly recommend calling this at the end of a test as part of test cleanup.

Further Reading

For further reading on syntax, refer to the following

Starting a project

Forge

Forge is a command line tool that provides tasks for creating f# projects. In this post, Forge will be used to create the project and add dependencies.
To install forge, follow this link and instructions.

After forge is installed, open a command prompt or terminal and type forge.

If the installation was successful, then the following should be printed to console

Forge should be run from solution/repository root. Please ensure you don't run it from folder containing other solutions

Do You want to continue? [Y/n]
Y

Initializing Forge... use -h or --help to see commands

/home/jeremy [-FORGE-]
λ

For manual installation of forge: Add the forge folder to the PATH environment variable then call forge from command line type forge.sh. This can be fixed with an alias. Execute (or add to .bashrc) alias forge="forge.sh

To exit Forge, use the command exit

Creating the project

Creating a project with forge is easy and simple. Execute the following command

forge new project nameOfProject

Then follow the onscreen prompt. .

When asked to select a template, type console

jeremy@jeremy-UX305UA ~/temp $ forge new project canopyexample

Forge should be run from solution/repository root. Please ensure you don't run it from folder containing other solutions

Do You want to continue? [Y/n]
Y
Enter project name:
> canopyexample
Enter project directory (relative to working directory):
>
Choose a template:
 - classlib
 - console
 - fslabbasic
 - fslabjournal
 - fsunit
 - pcl259
 - servicefabrichost
 - servicefabricsuavestateless
 - sln
 - suave
 - suaveazurebootstrapper
 - temp
 - websharperserverclient
 - websharperspa
 - websharpersuave
 - windows

> console
Generating project...
Creating /home/jeremy/temp/canopyexample

Adding canopy as a dependency

When Forge created the project, it added Paket, a dependency manager for .Net and mono projects. Paket brilliantly handles dependency versions and conflicts.

Forge can be used to add packages through paket.

To install canopy using paket, execute the following command

forge paket add nuget canopy project canopyexample

Replace canopyexample with the name of the project

This will download canopy from nuget and add canopy to the dependency list in paket.dependencies. The version number and all sub-dependency version numbers, are locked in a file named paket.lock

The First Test!

Creating the Test Module

Open the file that is named after the project that was created.

In my case this was ./canopyexample/canopyexample.fs

It should contain the following default code.

module canopyexample

[<EntryPoint>]
let main argv =
    printfn "%A" argv
    0 // return an integer exit code

Delete the code starting with [<EntryPoint>]
The file should look like

module canopyexample

Except with the name canopyexample replaced with the desired project name.

Opening Canopy

Before writing canopy code, canopy must be opened using the code open canopy

The file so far should look like

module canopyexample

open canopy

Writing a Test

The test will start after the line open canopy.

Define the test first

"JeremyBellows.com should have the words 'Jeremy Bellows' visible" &&& fun _ ->

F# is whitespace important. This means that whitespaces tell the compiler where code blocks start and end. Since the test definition starts an anonymous function using fun _ ->, then the code that belongs in that anonymous function needs to be indented 2 whitespaces.

"JeremyBellows.com should have the words 'Jeremy Bellows' visible" &&& fun _ ->
  start Chrome
  url "http://www.jeremybellows.com"
  displayed "Jeremy Bellows"
  click "Jeremy Bellows"
  on "http://www.jeremybellows.com"
  displayed "Jeremy Bellows"
  quit ()

The file so far should look like

module canopyexample

open canopy

"JeremyBellows.com should have the words 'Jeremy Bellows' visible" &&& fun _ ->
  start Chrome
  url "http://www.jeremybellows.com"
  displayed "Jeremy Bellows"
  click "Jeremy Bellows"
  on "http://www.jeremybellows.com"
  displayed "Jeremy Bellows"
  quit ()

Adding the Chrome Driver

Selenium uses a packaged chromedriver to run chrome. The chromedriver will need to be installed and canopy will need to be told where the file is.

First download the chromedriver for the desired operating system to the root folder of the project

Then add the followingline of code

canopy.configuration.chromeDir <- System.AppDomain.CurrentDomain.BaseDirectory

This will tell canopy to look in the application directory of the project for chrome.

canopy.configuration.chromeDir is a mutable variable and <- is the syntax for mutating a value.

Compiling the Tests

Before the tests can be executed, the program needs to know to run the tests registered using the &&& canopy operator.

To do so add run () after the test and without any indentation

The code should look like

module canopyexample

open canopy

"JeremyBellows.com should have the words 'Jeremy Bellows' visible" &&& fun _ ->
  start Chrome
  url "http://www.jeremybellows.com"
  displayed "Jeremy Bellows"
  click "Jeremy Bellows"
  on "http://www.jeremybellows.com"
  displayed "Jeremy Bellows"
  quit ()

run ()

In a command prompt or terminal that has the root folder of the project opened, execute ./build.cm for windows or ./build.sh for OSX/Linux.

Executing the tests

Once the tests are built, the executable is located in the folder ./build/

Windows

To execute on windows, open a command prompt and navigate to the project folder.

Execute the following command

./build/canopyexample.exe

OSX or Linux

To execute on OSX or Linux, open a terminal instance and navigate to the project folder.

Execute the following command

mono ./build/canopyexample.exe

Summary

Canopy is an excellent ui testing library that fits the needs of beginners and professionals. In this article, we learned how to setup an f# project using forge, add a dependency using paket, and how to write tests in canopy.

F#’s syntax helps reduce complexity in tests which improves quality and reduces difficulty when writing tests. For those looking for Selenium Alternatives, Canopy is a must.

The community is excellent and willing to help anyone who is struggling.

The maintainer and creator, Chris Holt, aka @lefthandedgoat, created a wonderful quick start video to help newcomers to canopy!

There is also the canopy start kit created by @lefthandedgoat which contains everything needed to quickly start a scalable canopy project.


Question? Reach out to me through twitter @JeremyBellows or to the f# community at #fsharp