Seleno V0.6 is released
Seleno V0.6 was just released. The main theme of this release was to make the API more consistent, fluent and extensible.
If you are an existing Seleno user then you can find the breaking changes and their fixes here.
TLDR - There were three main changes in this release:
- Methods on
UiComponent
were changed to properties to read more fluently. - The new
UiComponent
properties were made public to make it easier to interact with pages from test scripts. IElementAssert
interface API changed to allow for jQuery selectors and also for API consistency betweenIElementFinder
andIElementAssert
.
Fluency and open API
A lot of the interactions with the page objects in the previous releases were through methods. An example out of the documentation from the previous release:
public class SearchPage : Page
{
public SearchPage InputSearchTerm(string term)
{
Find()
.Element(By.Name("q"))
.SendKeys(term);
return this;
}
public ResultsPage Search()
{
return Navigate().To<ResultsPage>(By.Name("btnG"));
}
}
That Find
and Navigate
methods didn’t read well. They are now changed to properties so now you can write the same class as:
public class SearchPage : Page
{
public SearchPage InputSearchTerm(string term)
{
Find.Element(By.Name("q"))
.SendKeys(term);
return this;
}
public ResultsPage Search()
{
return Navigate.To<ResultsPage>(By.Name("btnG"));
}
}
We tried to make the API more fluent by changing methods to properties and also to make signature changes when needed:
Navigate().To -> Navigate.To
Find().Elements -> Find.Elements
AssertThatElements().DoNotExist -> AssertThatElements.DoNotExist
An example of a signature change is ExecuteScript
Execute().ExecuteScript -> Execute.Script
It’s also worth noting that all these new properties are made public. This isn’t necessarily something we want to promote and you should try to avoid calling public page properties from your test script; but now you can if you really need to. For example, before, to read a page using view model you would have to first create a page wrapper; e.g.
public class RegisterPage : Page<RegisterModel>
{
public RegisterModel Model
{
get { return Read.ModelFromPage(); }
}
}
And then you could use this class from your test scripts to assert on the values:
[Test]
public void CanSeeRegistrationDetails()
{
var registerPage = Host.Instance
.NavigateToRegisterPage<RegisterationController, RegisterPage>(x => x.Index());
Assert.AreEqual("Mehdi", registerPage.Model.FirstName);
}
Now that the properties are public you can avoid that wrapper if you REALLY REALLY need to. So the same example could be implemented as:
[Test]
public void CanSeeRegistrationDetails()
{
var registerPage = Host.Instance
.NavigateToRegisterPage<RegisterationController, Page<RegisterModel>>(x => x.Index());
Assert.AreEqual("Mehdi", registerPage.Read.ModelFromPage().FirstName);
}
I say if you really have to because all page interactions should be encapsulated in a page class and your test scripts should not interact with your pages directly.
API consistency for assertions
There is an interface called IElementFinder
which is returned by the Find
property of UiComponent
which allows you to find things on your page:
public interface IElementFinder
{
IWebElement Element(By findExpression, TimeSpan maxWait = default(TimeSpan));
IWebElement Element(Locators.By.jQueryBy jQueryFindExpression, TimeSpan maxWait = default(TimeSpan));
IEnumerable<IWebElement> Elements(By findExpression, TimeSpan maxWait = default(TimeSpan));
IWebElement OptionalElement(By findExpression, TimeSpan maxWait = default(TimeSpan));
IWebElement OptionalElement(Locators.By.jQueryBy jQueryFindExpression, TimeSpan maxWait = default(TimeSpan));
}
You can see the complete source code here.
There is a similar interface called IElementAssert
which is in charge of assertions; but the API was inconsistent and incomplete. This has now changed to look and behave the same as IElementFinder
:
public interface IElementAssert
{
IElementFinder Find { get; }
IElementAssert DoNotExist(By findExpression, string message = null, TimeSpan maxWait = default(TimeSpan));
IElementAssert DoNotExist(PageObjects.Locators.By.jQueryBy findExpression, string message = null, TimeSpan maxWait = default(TimeSpan));
IElementAssert Exist(By findExpression, string message = null, TimeSpan maxWait = default(TimeSpan));
IElementAssert Exist(PageObjects.Locators.By.jQueryBy findExpression, string message = null, TimeSpan maxWait = default(TimeSpan));
IElementAssert ConformTo(By findExpression, Action<IEnumerable<IWebElement>> assertion, TimeSpan maxWait = default(TimeSpan));
IElementAssert ConformTo(PageObjects.Locators.By.jQueryBy findExpression, Action<IEnumerable<IWebElement>> assertion, TimeSpan maxWait = default(TimeSpan));
}
This new interface also allows you to use jQueryBy
selector which allows you to use your good old jQuery selectors for element targeting.
Some resources to get started
If you are new to Seleno then go grab it while it’s hot. Although pre V1 it’s quite stable and has been in use by many teams for quite some time now. Seleno allows you to write maintainable UI tests. As a bonus point if you are using ASP.Net MVC it significantly reduces the amount of code you have to write to interact with your page by hooking into MVC infrastructure and using your view models to interact with your pages.