Selenium is great tool for acceptance testing of web applications, but using it in “raw” style (record tests, fix recorded script and playback) causes a lot of drawbacks in future. It is just impossible to maintain such tests. Therefore i would like to share my and Valera’s experience of implementing acceptance tests through UI.
Along with writing posts i will implements all i described in design-of-selenium-tests-for-asp-net project, so you can find all samples there.
First two things which are needed to be introduced are Page Object and Navigator patterns:
Page Object
Page Objects is “must have” pattern in automated testing through UI. Without it tests becomes too dependent from html structure, developers have to fix UI tests after each UI change. Imaging how frustrating to fix all 100 test cases in Smoke test, after inserting div around login form (All tests cases included login procedure of course)!
The idea of Page Object is simple - Encapsulate all calls to controls on page:
And it contain only methods and properties, which delegate calls to Selenium framework using hardcoded IDs, or xPathes:
namespace Infrastructure.Tests.PageObjects
{
public class LoginPage : PageBase
{
public LoginPage() : base("Login.aspx")
{ }
public void LoginButtonClick()
{
Selenium.Click("btnLogin");
}
public string ErrorMessage
{
get { return Selenium.GetText("lbMessage"); }
}
public string Password
{
set { Selenium.Type("tbxPassword", value); }
}
public string UserName
{
set { Selenium.Type("tbxLogin", value); }
}
}
}
All test cases should use Page Objects to access controls on page. If something will be changed in html, it will be needed to change only Page Objects to fix all test cases.
Example of Page Object pattern in design-of-selenium-tests-for-asp-net sample project is located here.Navigator
Navigator represents entity, which monitors what page resides in browser. All redirects, events which can causes redirects, postbacks, popups, ajax calls should be performed using Navigator. Responsibilities of Navigator includes:
- Assert that redirect was performed to the right place. It means after click on “Home” link in menu user must be redirected to HomePage, not to Login or somewhere else.
- Assert that redirect or postback is not caused server error and if caused, then gather information about error and include it in the error message of failed test.
- Prevent appearing of confusing errors like “Control with ID ‘bla-bla-bla’ was not found”. Often tests includes scenarios when it is needed to be redirected on some page and find control on it. But if page throws exception, there will be no controls on it – just error message.
- Encapsulate all “wait” activities. It should wait for browser to load whole page before accessing it, wait for completing AJAX requests and of course manage timeouts.
Interface of Navigator:
using System;
namespace Tests.SmokeTest.Core
{
public interface INavigator
{
TT Open<tt>() where TT : PageBase, new();
TT Navigate<tt>(Action action) where TT : PageBase, new();
}
}
Usage of Navigator:
var login = _navigator.Open<loginpage>();
login.User.SetText("admin");
login.Password.SetText("incorrectPwd");
login = _navigator.Navigate<loginpage>(login.ClickLogin);
Example of Navigator pattern in design-of-selenium-tests-for-asp-net sample project is located here.
After introducing this patterns tests becomes clear, easy to read and easy to implement. Just check it out:
using NUnit.Framework;
using Tests.SmokeTest.Core;
using Tests.SmokeTest.PageObjects;
namespace Tests.SmokeTest.Tests
{
[TestFixture]
public class LoginTest
{
private SeleniumScope _scope;
private INavigator _navigator;
[TestFixtureSetUp]
public void Setup()
{
_scope = new SeleniumScope();
_navigator = new Navigator(_scope.Selenium);
}
[TestFixtureTearDown]
public void TearDown()
{
_scope.Dispose();
}
[Test]
public void LoginSuccessWithCorrectPassword()
{
var login = _navigator.Open<LoginPage>();
login.User.SetText("admin");
login.Password.SetText("god");
_navigator.Navigate<HomePage>(login.ClickLogin);
}
[Test]
public void LoginWrongWithWrongPassword()
{
var login = _navigator.Open<LoginPage>();
login.User.SetText("admin");
login.Password.SetText("incorrectPwd");
login = _navigator.Navigate<LoginPage>(login.ClickLogin);
Assert.That(login.Message.GetText(), Is.EqualTo("Username and password do not match."));
}
}
}
Next post will describe how to autogenerate Page Object using T4 templates.
0 comments:
Post a Comment