Introduction

FluentAutomation is a simple, powerful automated testing framework for web applications. It can be used with Selenium WebDriver or WatiN to test all sorts of browsers and devices.

In this documentation, it is our intent to cover all the basics of the API and important classes/functions. If you notice any issues or missing content please create an issue on GitHub as soon as possible.

Getting Started

FluentAutomation is implemented using one of two supported automation providers - Selenium WebDriver or WatiN. Selenium is the preferred provider and the most developed. WatiN is provided as an alternative method of automation Internet Explorer. PhantomJS support has been moved into the Selenium package as a browser target.

You'll need a unit test framework as well. I use either xUnit.net or NUnit in most projects. MSTest (the default in Visual Studio Test Projects) is also supported. We don't exhaustively test on every unit test library out there so let us know if your favorite framework doesn't work!

Once you've decided which provider to use and have your test framework ready, it is super easy to start testing with FluentAutomation -- use NuGet!

The commands to the right can help you install via the NuGet Package Manager Console or you can just search for FluentAutomation in the 'Manage NuGet Packages' dialog within Visual Studio. NuGet will handle downloading all the dependencies and setting up the project.

If you're using xUnit.net, copy the sample test on the right into a new class file and you're ready to go!

Test Setup

Your test classes need to inherit from FluentAutomation.FluentTest and call your providers appropriate Bootstrap method. In most test frameworks you'll do this in the constructor of your class, as shown in the sample.

For Selenium, the Bootstrap method takes the browser target as an argument. You can pass multiple browsers if you would like to run the same test in each.

/*-------- NuGet Package Manager Console --------*/

// Install the Selenium WebDriver provider
Install-Package FluentAutomation.SeleniumWebDriver

// Install the WatiN provider
Install-Package FluentAutomation.WatiN

/*--------------- SampleTests.cs ---------------*/

using FluentAutomation;  
using Xunit;

namespace TestProject  
{
    public  class SampleTests : FluentTest
    {
        public SampleTests()
        {
            SeleniumWebDriver.Bootstrap(
                SeleniumWebDriver.Browser.Chrome
            );
        }
 
        [Fact]
        public void Test1()
        {
            I.Open("http://google.com/");
            // get busy testing!
        }
    }
}
// TODO - Visual Basic Code Sample  

Settings

Most of the configurable settings are located on the static object FluentAutomation.Settings and can be set anywhere within your project.

TimeSpan DefaultWaitTimeout
Length of time an unargumented I.Wait() will wait before continuing.

TimeSpan DefaultWaitUntilThreadSleep
Length of time to wait before re-evaluating the condition passed to WaitUntil.

TimeSpan DefaultWaitUntilTimeout
Length of time to wait before assuming the WaitUntil condition will not be met.

bool ExpectIsAssert
Whether or not I.Expect throws exceptions that will stop a test from continuing.

bool MinimizeAllWIndowsOnTestStart
Whether or not we ask the Win32 API to minimize other windows before starting a test.

bool ScreenshotOnFailedAction
Whether or not we automatically take a screenshot when an action fails.

bool ScreenshotOnFailedExpect
Whether or not we automatically take a screenshot when an Assert or Expect fails.

string ScreenshotPath
Path to store screenshots on the filesystem.

string UserTempDirectory
Path used for temporary storage. Usually not necessary to change.

bool WaitOnAllCommands
Whether or not we enable implicit waits on all comamnds/actions using I.WaitUntil.

bool WaitOnAllExpects
Whether or not we enable implicit waits on all asserts or expects using I.WaitUntil.

int WindowHeight
Height of the browser window, if null will use system/profile default.

int WindowWidth
Width of the browser window, if null will use system/profile default.

Action<FluentExpectFailedException> ExpectFailedCallback
Method executed when Settings.ExpectIsAssert is true and an Expect fails.

// Settings - All default values  
Settings.DefaultWaitTimeout = TimeSpan.FromSeconds(1);  
Settings.DefaultWaitUntilTimeout = TimeSpan.FromSeconds(30);  
Settings.DefaultWaitUntilThreadSleep = TimeSpan.FromMilliseconds(100);  
Settings.ExpectIsAssert = true; // changing in v2.4 to false  
Settings.MinimizeAllWindowsOnTestStart = false;  
Settings.ScreenshotOnFailedAction = false;  
Settings.ScreenshotOnFailedExpect = false;  
Settings.ScreenshotPath = Path.GetTempPath();  
Settings.UserTempDirectory = Path.GetTempPath();  
Settings.WaitOnAllCommands = true;  
Settings.WaitOnAllExpects = true;  
Settings.WindowHeight = null;  
Settings.WindowWidth = null;

Settings.ExpectFailedCallback = (c) => {  
    var fluentException = c.InnerException as FluentException;
    if (fluentException != null)
        Trace.WriteLine(fluentException.Message);
    else
        Trace.WriteLine(c.Message);  
};
// TODO - Visual Basic Code Sample  

Multi-browser Testing

Feature Status: Beta

Many projects require that you validate your application in multiple browsers. Over the years we've had many different methods for achieving this, but now we've made it as simple as we can.

Simply pass in multiple browser values to the Bootstrap call and we'll run the tests in parallel.

Methods that return references to page elements such as I.Find will not work when testing with multiple browsers. There are known issues with certain functions triggering this issue inappropriately, notablyI.Select.From(). We are working on it!

using FluentAutomation;  
using Xunit;

namespace TestProject  
{
    public  class SampleTests : FluentTest
    {
        public SampleTests()
        {
            // Execute tests in this class with Chrome, Firefox and IE
            SeleniumWebDriver.Bootstrap(
                SeleniumWebDriver.Browser.Chrome,
                SeleniumWebDriver.Browser.Firefox,
                SeleniumWebDriver.Browser.InternetExplorer
            );
        }
 
        [Fact]
        public void Test1()
        {
            I.Open("http://google.com/");
            // get busy testing!
        }
    }
}
// TODO - Visual Basic Code Sample  

Sticky Sessions

Feature Status: Beta

One of those features built on an airplane (the best way to build new things, I think) -- Enables a set of tests to reuse the same browser instance between tests.

This can result in much faster execution times as compared to the standard method of creating a new browser instance for each test. Some Applications-under-test can experience intermittent issues in this configuration so it is not the default.

// All tests executed after this will share browser instances  
FluentSession.EnableStickySession();

// Stop sharing browser instances.
FluentSession.DisableStickySession();

// Pass the current tests session in and use it for other tests
// (in the scope of a test method)
FluentSession.SetStickySession(this.Session);  
// TODO - Visual Basic Code Sample  

PageObjects

Anyone who has done any serious amount of automated testing will tell you that the largest challenge we all face is fragile, hard to maintain tests.

To combat this, it can be useful to use PageObject to group your actions/expects/asserts. This gives you functional code that can be reused in any number of tests.

With the new first-class PageObject support, we provide some simple but useful built-in functions such as Go and Switch. Also included, a validation function tied to the this.At property.

Any time a PageObject navigation is triggered, or any time Switch is used, the included At functino will execute providing an easy hook to make sure all tests execute with the underlying browser and application in the same state, every time.

If you have any ideas for or comments on the new PageObject functionality, let us know! Its a new feature and we'd love some feedback.

public class BingSearchPage : PageObject  
{
    public BingSearchPage(FluentTest test)
        : base(test)
    {
        Url = "http://bing.com/";
        At = () =>; I.Expect.Exists(SearchInput);
    }
 
    public BingSearchResultsPage Search(string searchText)
    {
        I.Enter(searchText).In(SearchInput);
        I.Press("{ENTER}");
        return this.Switch();
    }
 
    private const string SearchInput = "input[title='Enter your search term']";
}

public class BingSearchResultsPage : PageObject  
{
    public BingSearchResultsPage(FluentTest test)
        : base(test)
    {
        At = () => I.Expect.Exists(SearchResultsContainer);
    }
 
    public BingSearchResultsPage FindResultUrl(string url)
    {
        I.Expect.Exists(string.Format(ResultUrlLink, url));
        return this;
    }
 
    private const string SearchResultsContainer = "#b_results";
    private const string ResultUrlLink = "a[href='{0}']";
}

public class SampleTest : FluentTest  
{
    public SampleTest()
    {
        SeleniumWebDriver.Bootstrap(SeleniumWebDriver.Browser.Chrome);
    }
 
    [Fact]
    public void SearchForFluentAutomation()
    {
        new BingSearchPage(this)
            .Go()
            .Search("FluentAutomation")
            .FindResultUrl("http://fluent.stirno.com/blog/FluentAutomation-scriptcs/");
    }
}
// TODO - Visual Basic Code Sample  

Method Chaining

As part of trying to provide the best fluent syntax we can, we now have method chaining on all "terminating methods". These are the bits that actually execute or do something, rather than just configure the next step.

This provides a pretty clean, very simple way to push your tests towards AAA (Arrange/Act/Assert). As you can see to the right, clean and tight code.

I.Open("http://automation.apphb.com/forms")  
    .Select("Motorcycles").From(".liveExample tr select:eq(0)")
    .Select(2).From(".liveExample tr select:eq(1)")
    .Enter(6).In(".liveExample td.quantity input:eq(0)")
    .Expect
        .Text("$197.72").In(".liveExample tr span:eq(1)")
        .Value(6).In(".liveExample td.quantity input:eq(0)");
// TODO - Visual Basic Code Sample  

Remote WebDriver

We fully support connecting to remote WebDriver instances (including Selenium Grid) as of the v2.2 bits. Obviously this is only supported in the SeleniumWebDriver provider.

This is accomplished by passing a URI and settings to the Bootstrap method in your constructor. Remote functionality becomes useful when connecting to Selenium Grid or if you want to automate a browser that doesn't run on your primary machine.

Additionally, there is support for passing a Dictionary<string, object> of capabilities.

using FluentAutomation;  
using System;

namespace TestProject  
{
    public class MacSafariTests : FluentTest
    {
        public MacSafariTests()
        {
            SeleniumWebDriver.Bootstrap(
                new Uri("http://mac/wd/hub"), SeleniumWebDriver.Browser.Safari
            );
        }
    }
}
// TODO - Visual Basic Code Sample  

scriptcs

Access to FluentAutomation inside of scriptcs is trivially easy and very powerful. We have a custom Script Pack that provides access to the functionality you expect.

To get started, install scriptcs (per instructions on their site). The commands to the right will get a directory ready for scriptcs testing.

Create a new script, test.csx for example. We'll require in the Script Pack, Bootstrap our browser and get testing.

The new script can be run very easily a Command Prompt with: scriptcs test.csx. You'll see some debugging information from the browser and/or Selenium -- no worries -- and then you'll see the test execute and you'll get results.

// prepare a new directory for your tests  
mkdir tests  
scriptcs -install ScriptCs.FluentAutomation  
scriptcs -install FluentAutomation.SeleniumWebDriver

// First test!
var Test = Require()  
    .Init()
    .Bootstrap("Chrome")
    .Config(settings => {
        // Easy access to FluentAutomation.Settings values
        settings.DefaultWaitUntilTimeout = TimeSpan.FromSeconds(1);
    });

Test.Run("Hello Google", I => {  
    I.Open("http://google.com");
});
// TODO - Visual Basic Code Sample  

Actions

We interchangeable use the terms Action and Command to refer to the same thing. This is because of terminology changes throughout the lifetime of the project.

Actions are the units of work which manipulate state of the browser or the page.

I.Open

Open and navigate the web browser to the specified URL or Uri. Using a Uri can be valuable to validate your URI fragment before using it in a test.

// Open browser via string/URL  
I.Open("http://google.com");

// Open browser via URI
I.Open(new Uri("http://google.com"));  
// TODO - Visual Basic Code Sample  

I.Enter

Primary method of entering text into inputs and textareas. Automatically calls ToString() on non-string values to simplify tests.

Using WithoutEvents() will cause the value to be set via JavaScript and will not trigger keyup/keydown events in the browser.

// Enter text into element  
I.Enter("FluentAutomation").In("#searchBox");

// Enter text without keyup/keydown events
I.Enter("FluentAutomation").WithoutEvents().In("#searchBox");

// Enter an integer into element
I.Enter(6).In("#quantity");

// Enter text using cached reference to element
var element = I.Find("#searchBox");  
I.Enter("FluentAutomation").In(element);  
// TODO - Visual Basic Code Sample  

I.Append

Append text to the current values of inputs and textareas. Automatically calls ToString() on non-string values to simplify tests.

Using WithoutEvents() will cause the value to be set via JavaScript and will not trigger keyup/keydown events in the browser.

// Append text onto element value  
I.Append("FluentAutomation").In("#searchBox");

// Append text without keyup/keydown events
I.Append("FluentAutomation").WithoutEvents().In("#searchBox");

// Append an integer onto element value
I.Append(6).In("#quantity");

// Append text using cached reference to element
var element = I.Find("#searchBox");  
I.Append("FluentAutomation").In(element);  
// TODO - Visual Basic Code Sample  

I.Select

Primary method of selecting items in <SELECT> elements found via selector or cached reference. Selection can be done using <OPTION> value, text or index.

Selecting via Text/string will fall back to Value matching if no match is found. This simplifies the API for most users but may be confusing. If you need to guarantee the selection was based on value use the Option overload.

// Select option by Text  
I.Select("MN").From("#states");

// Select option by index
I.Select(12).From("#states");

// Select option by value
I.Select(Option.Value, 9999).From("#numbers");  
// TODO - Visual Basic Code Sample  

I.Click

Click an element by selector, coordinates or cached reference. Optionally provide an offset to click relative to the item.

// Click by element selector  
I.Click("#searchBox");

// Click by x, y coordinates
I.Click(10, 100);

// Click by relative offset from element selector
I.Click("#searchBox", 10, 100);

// Click using cached reference to element.
var element = I.Find("#searchBox");  
I.Click(element);  
// TODO - Visual Basic Code Sample  

I.DoubleClick

Double-click an element by selector, coordinates or cached reference. Optionally provide an offset to click relative to the item.

// DoubleClick by element selector  
I.DoubleClick("#searchBox");

// DoubleClick by x, y coordinates
I.DoubleClick(10, 100);

// DoubleClick by relative offset from element selector
I.DoubleClick("#searchBox", 10, 100);

// DoubleClick using cached reference to element.
var element = I.Find("#searchBox");  
I.DoubleClick(element);  
// TODO - Visual Basic Code Sample  

I.RightClick

Right-click an element by selector, coordinates or cached reference. Optionally provide an offset to click relative to the item.

// RightClick by element selector  
I.RightClick("#searchBox");

// RightClick by x, y coordinates
I.RightClick(10, 100);

// RightClick by relative offset from element selector
I.RightClick("#searchBox", 10, 100);

// RightClick using cached reference to element.
var element = I.Find("#searchBox");  
I.RightClick(element);  
// TODO - Visual Basic Code Sample  

I.Focus

Set the browser's current focus to a specified element or cached reference to an element.

// Set browser focus by selector  
I.Focus("#searchBox");

// Set browser focus using cached reference to element
var element = I.Find("#searchBox");  
// TODO - Visual Basic Code Sample  

I.Hover

Cause the mouse to hover over a specified element, coordinates or position relative to an element.

// Hover over element  
I.Hover("#searchBox");

// Hover over x, y coordinates
I.Hover(10, 100);

// Hover over relative offset from element selector
I.Hover("#searchBox", 10, 100);

// Hover using cached reference to element.
var element = I.Find("#searchBox");  
I.Hover(element);  
// TODO - Visual Basic Code Sample  

I.Press

Triggers a single OS level keypress event. This method will send events to whatever the active window is at the time its trigger, currently not guaranteed to be the actual browser window. Use with caution.

The intended use is for interactive with elements that steal focus or are not a part of the DOM such as Flash.

Several keys require special values to be used. Refer to the Windows Forms SendKeys documentation for valid values.

// Press Tab key  
I.Press("{TAB}");  
// TODO - Visual Basic Code Sample  

I.Type

Type a string, one character at a time using OS level keypress events. This functionality will send keypress events to whatever the active window is at the time its trigger, currently not guaranteed to be the actual browser window. Use with caution.

The intended use is for interactive with elements that steal focus or are not a part of the DOM such as Flash.

Type does not support the use of special key values.

// Type string  
I.Type("FluentAutomation");  
// TODO - Visual Basic Code Sample  

I.Wait

Wait for a specified period of time before continuing the test. Method accepts a number of seconds or a TimeSpan. Not guaranteed to be exact.

In most cases, your tests will be less fragile if you can utilize I.WaitUntil instead.

// Wait for 10 seconds  
I.Wait(10);

// Wait for 500 milliseconds
I.Wait(TimeSpan.FromMilliseconds(500));  
// TODO - Visual Basic Code Sample  

I.WaitUntil

Recommended method of waiting in tests. Conditional wait using anonymous functions that either return true / false or throw an Exception when the condition has not been met. Useful when content on the page is loaded dynamically or changes state during interactions.

If the condition has not been met, a timed wait will be executed before testing the condition again. The duration if this wait is set in Settings.DefaultWaitUntilThreadSleep.

The condition must succeed within the time set in Settings.DefaultWaitUntilTimeout or the test will fail.

Important Note: Most actions have implicit WaitUntil functionality built-in. Before adding, be sure your test needs it.

// WaitUntil element exists  
I.WaitUntil(() => I.Assert.Exists("#searchBar")));

// WaitUntil element has attribute 'data-loaded'
I.WaitUntil(() =>  
    I.Find("#searchBar")()
        .Attributes
        .Get("data-loaded") == "true"
);
// TODO - Visual Basic Code Sample  

I.Find

Get a factory reference to an element. Returns a function that can be evaluated to return access to the underlying element. Used internally by all functions that target elements.

A second method, I.FindMultiple, exists to retrieve a collection of elements at once. It provides the same factory function but returns an IEnumerable instead.

Often this function is used to break through the abstraction and get direct access to the providers element representation. This can be necessary in some cases but using I.Find in this way is discouraged.

Warning: If you intend to cache an element, cache this function not its result. The result is not kept up to date with the current state of the page.

// Find element by selector  
var element = I.Find("#searchBox");

// Get reference to underlying IWebElement (Selenium)
var webElement = element() as OpenQA.Selenium.IWebElement;

// Get reference to underlying WatiN.Core.Element (WatiN)
var webElement = element() as WatiN.Core.Element;

// Find a collection of elements matching selector
var listItems = I.FindMultiple("li");  
// TODO - Visual Basic Code Sample  

I.Drag

Drag & drop works with elements, coordinates and offsets. In the next version, the offset functionality will look a bit less... stupid. Sorry about that.

// Drag one element to another  
I.Drag("#drag").To("#drop");

// Drag one coordinate to another
I.Drag(100, 100).To(500, 500);

// Drag from element offset to another element
I.Drag(I.Find("#drag"), 50, 50).To("#drop");

// Drag from element to another elements offset
I.Drag("#drag").To(I.Find("#drop", 100, 30));  
// TODO - Visual Basic Code Sample  

I.TakeScreenshot

Grab a quick screenshot of the current browser window and save it to disk. The screenshot path is configurable via Settings.ScreenshotPath.

// Take Screenshot  
I.TakeScreenshot("LoginScreen");  
// TODO - Visual Basic Code Sample  

I.Upload

Upload a file using an <input type="file"> on the current page. The provided path must be absolute and point to the file you want to upload.

This has been used with several Flash uploaders without issue. Your mileage may vary.

// Upload LoginScreen.jpg  
I.Upload("input[type='file'].uploader", @"C:\LoginScreen");  
// TODO - Visual Basic Code Sample  

Expect/Assert

As of v2.3 proper assertions were added to FluentAutomation.

Fundamentally Expect and Assert work exactly the same. The methods and their signatures are identical. The difference is that, when Settings.ExpectIsAssert is set to false, Assert will throw an Exception and fail a test. In comparison, Expect will attempt to log the failure using the Action provided in Settings.ExpectFailedCallback and allow the test to continue

For ease of maintaining the documentation, we will document Assert methods only. Unless otherwise noted, Expect methods are identical.

Warning: The current default value of Settings.ExpectIsAssert is true. This will change in v2.4. If you want to guarantee your test behavior doesn't change, set the value in your project before upgrading.

I.Assert.Exists

Assert that an element is on the page.

// Element on page  
I.Assert.Exists("#searchBar");  
// TODO - Visual Basic Code Sample  

I.Assert.Count

Assert that we have a certain count of items matching a selector.

// 1 search bar on page.  
I.Assert.Count(1).Of("#searchBar");  
// TODO - Visual Basic Code Sample  

I.Assert.Value

Assert that an element matching selector has the specified value. Works with <INPUT>, <TEXTAREA> and <SELECT>

Supports anonymous functions that return true or false.

// Dropdown has value of 10.  
I.Assert.Value(10).In("#quantity");

// Value starts with 'M'
I.Assert.Value((value) => value.StartsWith("M")).In("#states");  
// TODO - Visual Basic Code Sample  

I.Assert.Text

Assert that an element matching selector has the specified text. Works with any DOM element that has innerHTML or can provides its contents/value via text.

Supports anonymous functions that return true or false.

// Header tag set to FluentAutomation  
I.Assert.Text("FluentAutomation").In("header");

// Content longer than 50 characters
I.Assert.Text((text) => text.Length > 50).In("#content");  
// TODO - Visual Basic Code Sample  

I.Assert.Class

Assert that an element matching selector has the specified class.

// Has btn-primary class  
I.Assert.Class("btn-primary").Of("header");  
// TODO - Visual Basic Code Sample  

I.Assert.Url

Assert that the browser has the specified Url. If using the string or Uri overloads, the match must be exact.

Supports anonymous functions that return true or false. Particularly useful on pages that modify the URL via hashtags or other mechanisms.

// At #assert-url on docs  
I.Assert.Url("http://fluent.stirno.com/docs/#assert-url");

// Verify we're on SSL
I.Assert.Url((uri) => uri.Scheme == "https");  
// TODO - Visual Basic Code Sample  

I.Assert.Throws

Assert that an Exception should be thrown by the anonymous function. Useful for negative assertions such as testing that something is not present.

// Page has no errors  
I.Assert.Throws(() => I.Assert.Exists(".error"));  
// TODO - Visual Basic Code Sample  

I.Assert.True

Assert that an anonymous function should return true. Use with I.Find to fail tests properly if conditions are not met.

// Element is a select box  
var element = I.Find("select");  
I.Assert.True(() => element().IsSelect);  
// TODO - Visual Basic Code Sample  

I.Assert.False

Assert that an anonymous function should return false. Use with I.Find to fail tests properly if conditions are not met.

// Element is not a select box  
var element = I.Find("input");  
I.Assert.False(() => element().IsSelect);  
// TODO - Visual Basic Code Sample