Professional Documents
Culture Documents
Coypu supports browser automation in .Net to help make tests readable, robust,
fast to write and less tightly coupled to the UI. If your tests are littered with
sleeps, retries, complex XPath expressions and IDs dug out of the source with
browser developer tools then Coypu might help.
Coypu is on Nuget:
Coypu is
A robust wrapper for browser automation on .net platform for Selenium
WebDriver that eases automating ajax-heavy websites and reduces
coupling to the HTML, CSS & JS
A more intuitive DSL for interacting with the browser in the way a human
being would, inspired by the ruby framework Capybara
- http://github.com/jnicklas/capybara
Using Coypu
Browser session
Open a browser session like so:
browser.Dispose();
or:
Configuration
If you don't specify any of these, Coypu will default to http, localhost and port 80.
Driver
If you want to configure these at runtime you could replace the following strings
with strings read from your environment / configuration:
sessionConfiguration.Driver =
Type.GetType("Coypu.Drivers.Selenium.SeleniumWebDriver, Coypu");
sessionConfiguration.Browser = Drivers.Browser.Parse("firefox");
Selenium WebDriver
Chrome
You will need the chromedriver.exe on your PATH or in the bin of your test
project. We recommend adding the nuget
package Selenium.WebDriver.ChromeDriver to your project.
Firefox
Edge
You will need Microsoft's WebDriver. How you install this depends on your
version of Windows 10.
Internet Explorer
You will need the new standalone InternetExplorerDriver.exe in your PATH or in
the bin of your test project. We recommend adding the nuget
package Selenium.WebDriver.IEDriver package to your project.
Most of the methods in the Coypu DSL are automatically retried on any driver
error until a configurable timeout is reached. It just catches exceptions and retries
-- mainly the Coypu.Drivers.MissingHtmlException that a driver should throw
when it cannot find something, but also any internal driver errors that the driver
might throw up.
This is a rather blunt approach that goes well beyond WebDriver's ImplicitWait,
for example, but the only truly robust strategy for heavily asynchronous websites,
where elements are flying in and out of the DOM constantly, that I have found.
sessionConfiguration.Timeout = TimeSpan.FromSeconds(1);
sessionConfiguration.RetryInterval = TimeSpan.FromSeconds(0.1);
Visible elements
Coypu drivers filter out any elements that are not visible on the page -- this
includes hidden inputs.
Non-visible elements can get in the way of finding the elements that we are really
looking for and cause often errors when trying to interact with them.
What we are really trying to do here is interact with the browser in the way that a
human would. It's probably best to avoid hacking around with elements not
accessible to the user where possible to avoid invalidating our tests in any case.
However...
If you really need this for some intractable problem where you cannot control the
browser without cheating like this, then there
is sessionConfiguration/options.ConsideringInvisibleElements =
true which overrides this restriction.
If there's something you need that's not part of the DSL then please you may
need to dive into the native driver which you can always do by casting the native
driver to whatever underlying driver you know you are using:
But if you need to do this, please consider forking Coypu, adding what you need
and sending a pull request.
DSL
browser.Visit("/used-cars");
browser.GoBack();
browser.GoForward();
browser.Title
Completing forms
Form fields are found by label text, id, name (except radio buttons), placeholder
or radio button value
// Drop downs
browser.Select("toyota").From("make");
// Text inputs
browser.FillIn("keywords").With("hybrid");
// File inputs
browser.FillIn("Avatar").With(@"c:\users\adiel\photos\avatar.jpg");
// Checkboxes
browser.Check("Additional ads")
browser.Uncheck("Additional ads")
// Checkboxes
browser.FindCss("input[type=checkbox].additional-ads").Check();
browser.FindCss("input[type=checkbox].additional-ads").Uncheck();
or
Clicking
browser.ClickButton("Search");
browser.ClickButton("search-used-vehicles");
browser.ClickLink("Reset search");
In this example, due to the way Coypu defers execution of finders, the FindCss
will also be retried, should the Click fail. For example if the DOM is shifting under
the driver's feet, the link may have become stale after it is found but before the
click is actioned while part of the page is reloaded.
This introduces the idea of Scope. The browser.Find methods return a Scope on
which you may perform actions, or make further scoped queries. There is more
on scope below.
The last way to click is to pass an element you have already found directly
to Click():
var allToClick = browser.FindAllCss("span.clickable")
foreach(var element in allToClick)
{
browser.Click(element);
}
N.B. For Asp.Net Web Forms testing you may need this method:
browser.FindLink("Home").Id
browser.FindLink("Home").Text
browser.FindLink("Home")["href"]
browser.FindLink("Home")["rel"]
If you are expecting a particular state to be reached then you can describe this in
a predicate and Coypu will retry until it matches.
Usage
This will be respected everywhere that Coypu matches visible text, including
buttons, select options, placeholder text and so on, but not anywhere else, for
example when considering ids or names of fields.
Usage
browserSession.ClickButton("Close", new Options{Match =
Match.First});
// or
browserSession.ClickButton("Close", Options.First);
browserSession.ClickLink(text, options);
Hover
browser.FindCss("span#hoverOnMe").Hover();
Fieldsets / Sections
To find this:
<fieldset>
<legend>Advanced search</legend>
...
</fieldset>
use this:
<div>
<h2>Search results</h2>
...
</div>
or this:
<section>
<h3>Search results</h3>
...
</section>
use this:
Scope
When you want perform operations only within a particular part of the page, find
the scope you want then use this as the scope for further finds and interactions
as in the previous fieldset/section example.
advancedSearch.FillIn("First name").With("Philip");
advancedSearch.FillIn("Middle initial").With("J");
advancedSearch.FillIn("Last name").With("Fry");
advancedSearch.Click("Find");
The actual finding of the scope is deferred until the driver needs to interact with
or find any element inside the Scope. If the scope becomes stale at any time it
will be re-found.
In XPath the expression // means something very specific, and it might not be
what you think. Contrary to common belief, // means "anywhere in the
document" not "anywhere in the current context". As an example:
browser.FindXPath("//body").FindAllXPath("//script");
You might expect this to find all script tags in the body, but actually, it finds all
script tags in the entire document, not only those in the body! What you're
looking for is the .// expression which means "any descendant of the current
node":
browser.FindXPath("//body").FindAllXPath(".//script");
(from https://github.com/jnicklas/capybara#beware-the-xpath--trap)
To restrict the scope to a frame or iframe, locate the frame by its name,id, title or
the text of an h1 element within the frame:
To restrict the scope to a browser window (or tab), locate the window by its title
or name:
If no exact match is found Coypu will consider windows were the title contains
the supplied value
browser.Visit("InteractionTestsPage.htm");
Assert.That(button.Exists());
Assert.That(popUp, Shows.Content("I am a pop up window"));
popUp.ExecuteScript("self.close()");
Assert.That(button.Missing());
button.Click();
N.B. If you drop down into the native Selenium driver and use SwitchTo() (highly
unrecommended), bypassing Coypu's FindWindow(), Coypu will lose track of the
current window, so make sure to switch back to the previous window before
dropping back to Coypu.
Window size
If you are dealing with multiple windows, just call these on the correct scope:
browser.FindWindow("Pop Up Window").MaximiseWindow();
browser.ExecuteScript("document.getElementById('SomeContainer').inne
rHTML = '<h2>Hello</h2>';");
Querying
The positive queries above will wait up to the configured timeout for a matching
element to appear and return as soon as it does.
The negative versions will wait for the element NOT to be present:
Matchers
There are NUnit matchers for some of the queries above to help with your
assertions:
Assert.That(browser,
Shows.ContentContaining(Some","Words","Anywhere","in","the","documen
t"))
Assert.That(browser, Shows.CssContaining("ul.menu >
li","match","in","any","order"))
Assert.That(browser, Shows.AllCssInOrder("ul.menu >
li","has","exactly","these","matches"))
Assert.That(browser.FindField("total"), Shows.Value("147"));
Assert.That(browser.FindField("total"), Shows.No.Value("0"));
Inner/OuterHTML
If you just want to grab the inner or outer HTML of an element to do your own
queries and assertions you can use:
var outerHTML = browser.FindCss("table#myData").OuterHTML;
var innerHTML = browser.FindCss("table#myData").InnerHTML; // Will
exclude the surrounding <table> ... </table>
Dialogs
browser.AcceptDialog();
browser.CancelDialog();
Sometimes you just can't predict what state the browser will be in. Not ideal for a
reliable test, but if it's unavoidable then you can use the Session.FindState like
this:
var signedIn = new State(() => browser.HasContent("Signed in in
as:"));
var signedOut = new State(() => browser.HasContent("Please sign
in"));
if (browser.FindState(signedIn,signedOut) == signedIn)
{
browser.ClickLink("Sign out");
}
It will return as soon as the first from your list of states is found, and throw if
none of the states are found within the SessionConfiguration.Timeout
Avoid this:
if (browser.HasContent("Signed in in as:"))
{
...
}
Screenshots
If you can't get the quality of feedback from your tests you need to tell you
exactly why they are failing you might need to take a screenshot:
browser.SaveScreenshot(@"c:\screenshots\my_feature\my_scenario_2013-
06-18_16_53.jpg");
browser.FindWindow("Your Popup window").SaveScreenshot(etc.);
or constructing the Driver yourself and passing in your own Driver instance:
[Test]
public void CustomProfile()
{
var customProfile = new FirefoxProfile(); // Configure as you
wish
var customDriver = new
CustomFirefoxProfileSeleniumWebDriver(customProfile);
using (var custom = new BrowserSession(customDriver))
{
custom.Visit("http://www.featurist.co.uk/");
// etc.
}
// Or if you need to
}
When you pass a custom driver, the Driver and Browser settings in
ConfigurationSettings are ignored
Sauce Labs
Here is an example of using a custom driver to run tests in Sauce Labs (thanks to
@Br3ttl3y for this):
More tricks/tips
So, you are using Coypu but sometimes links or buttons still don't seem to be
clicked when you expect them to. Well there are a couple more techniques that
Coypu can help you with in this situation.
If the driver reports it had found and clicked your element successfully but
nothing happens then it may simply be that your app isn't wiring up events at the
right time. But if you have exhausted this angle and cannot fix the problem in the
site itself, then you could try a couple of things:
Tell Coypu to keep clicking at regular intervals until you see the result you
expect:
This is far from ideal as you are coupling the click to the expected result rather
than verifying what you expect in a separate step, but as a last resort we have
found this useful.
Tell Coypu to wait a short time between first finding links/buttons and
clicking them:
sessionConfiguration.WaitBeforeClick =
TimeSpan.FromMilliseconds(0.2);